blob: 56555be78f33d1f8669242cc93dd57c5e5964c59 [file] [log] [blame]
Georg Brandl0a7ac7d2008-05-26 10:29:35 +00001"""Generic interface to all dbm clones.
2
3Use
4
5 import dbm
6 d = dbm.open(file, 'w', 0o666)
7
8The returned object is a dbm.bsd, dbm.gnu, dbm.ndbm or dbm.dumb
9object, dependent on the type of database being opened (determined by
10the whichdb function) in the case of an existing dbm. If the dbm does
11not exist and the create or new flag ('c' or 'n') was specified, the
12dbm type will be determined by the availability of the modules (tested
13in the above order).
14
15It has the following interface (key and data are strings):
16
17 d[key] = data # store data at key (may override data at
18 # existing key)
19 data = d[key] # retrieve data at key (raise KeyError if no
20 # such key)
21 del d[key] # delete data stored at key (raises KeyError
22 # if no such key)
23 flag = key in d # true if the key exists
24 list = d.keys() # return a list of all existing keys (slow!)
25
26Future versions may change the order in which implementations are
27tested for existence, add interfaces to other dbm-like
28implementations.
29
30The open function has an optional second argument. This can be 'r',
31for read-only access, 'w', for read-write access of an existing
32database, 'c' for read-write access to a new or existing database, and
33'n' for read-write access to a new database. The default is 'r'.
34
35Note: 'r' and 'w' fail if the database doesn't exist; 'c' creates it
36only if it doesn't exist; and 'n' always creates a new database.
37"""
38
39__all__ = ['open', 'whichdb', 'error', 'errors']
40
41import io
42import os
43import struct
44import sys
45
46
47class error(Exception):
48 pass
49
50_names = ['dbm.bsd', 'dbm.gnu', 'dbm.ndbm', 'dbm.dumb']
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000051_defaultmod = None
52_modules = {}
53
Georg Brandlb17acad2008-05-28 08:43:17 +000054error = (error, IOError)
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000055
56
57def open(file, flag = 'r', mode = 0o666):
Georg Brandlb17acad2008-05-28 08:43:17 +000058 global _defaultmod
59 if _defaultmod is None:
60 for name in _names:
61 try:
62 mod = __import__(name, fromlist=['open'])
63 except ImportError:
64 continue
65 if not _defaultmod:
66 _defaultmod = mod
67 _modules[name] = mod
68 if not _defaultmod:
69 raise ImportError("no dbm clone found; tried %s" % _names)
70
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000071 # guess the type of an existing database
72 result = whichdb(file)
73 if result is None:
74 # db doesn't exist
75 if 'c' in flag or 'n' in flag:
76 # file doesn't exist and the new flag was used so use default type
77 mod = _defaultmod
78 else:
Amaury Forgeot d'Arcb5cf3012008-09-25 22:27:43 +000079 raise error[0]("need 'c' or 'n' flag to open new db")
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000080 elif result == "":
81 # db type cannot be determined
Amaury Forgeot d'Arcb5cf3012008-09-25 22:27:43 +000082 raise error[0]("db type could not be determined")
Georg Brandlb17acad2008-05-28 08:43:17 +000083 elif result not in _modules:
Amaury Forgeot d'Arcb5cf3012008-09-25 22:27:43 +000084 raise error[0]("db type is {0}, but the module is not "
85 "available".format(result))
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000086 else:
87 mod = _modules[result]
88 return mod.open(file, flag, mode)
89
90
Georg Brandl0a7ac7d2008-05-26 10:29:35 +000091def whichdb(filename):
92 """Guess which db package to use to open a db file.
93
94 Return values:
95
96 - None if the database file can't be read;
97 - empty string if the file can be read but can't be recognized
98 - the name of the dbm submodule (e.g. "ndbm" or "gnu") if recognized.
99
100 Importing the given module may still fail, and opening the
101 database using that module may still fail.
102 """
103
104 # Check for ndbm first -- this has a .pag and a .dir file
105 try:
106 f = io.open(filename + ".pag", "rb")
107 f.close()
108 # dbm linked with gdbm on OS/2 doesn't have .dir file
109 if not (ndbm.library == "GNU gdbm" and sys.platform == "os2emx"):
110 f = io.open(filename + ".dir", "rb")
111 f.close()
112 return "dbm.ndbm"
113 except IOError:
114 # some dbm emulations based on Berkeley DB generate a .db file
115 # some do not, but they should be caught by the bsd checks
116 try:
117 f = io.open(filename + ".db", "rb")
118 f.close()
119 # guarantee we can actually open the file using dbm
120 # kind of overkill, but since we are dealing with emulations
121 # it seems like a prudent step
122 if ndbm is not None:
123 d = ndbm.open(filename)
124 d.close()
125 return "dbm.ndbm"
Georg Brandlb17acad2008-05-28 08:43:17 +0000126 except IOError:
Georg Brandl0a7ac7d2008-05-26 10:29:35 +0000127 pass
128
129 # Check for dumbdbm next -- this has a .dir and a .dat file
130 try:
131 # First check for presence of files
132 os.stat(filename + ".dat")
133 size = os.stat(filename + ".dir").st_size
134 # dumbdbm files with no keys are empty
135 if size == 0:
136 return "dbm.dumb"
137 f = io.open(filename + ".dir", "rb")
138 try:
139 if f.read(1) in (b"'", b'"'):
140 return "dbm.dumb"
141 finally:
142 f.close()
143 except (OSError, IOError):
144 pass
145
146 # See if the file exists, return None if not
147 try:
148 f = io.open(filename, "rb")
149 except IOError:
150 return None
151
152 # Read the start of the file -- the magic number
153 s16 = f.read(16)
154 f.close()
155 s = s16[0:4]
156
157 # Return "" if not at least 4 bytes
158 if len(s) != 4:
159 return ""
160
161 # Convert to 4-byte int in native byte order -- return "" if impossible
162 try:
163 (magic,) = struct.unpack("=l", s)
164 except struct.error:
165 return ""
166
167 # Check for GNU dbm
168 if magic == 0x13579ace:
169 return "dbm.gnu"
170
171 ## Check for old Berkeley db hash file format v2
172 #if magic in (0x00061561, 0x61150600):
173 # return "bsddb185" # not supported anymore
174
175 # Later versions of Berkeley db hash file have a 12-byte pad in
176 # front of the file type
177 try:
178 (magic,) = struct.unpack("=l", s16[-4:])
179 except struct.error:
180 return ""
181
Georg Brandl170fb042009-05-17 08:42:58 +0000182 ## Check for BSD hash
183 #if magic in (0x00061561, 0x61150600):
184 # return "dbm.bsd"
Georg Brandl0a7ac7d2008-05-26 10:29:35 +0000185
186 # Unknown
187 return ""
188
189
190if __name__ == "__main__":
191 for filename in sys.argv[1:]:
192 print(whichdb(filename) or "UNKNOWN", filename)