1# !/usr/bin/env python
2"""Guess which db package to use to open a db file."""
3
4import os
5import struct
6import sys
7
8try:
9    import dbm
10    _dbmerror = dbm.error
11except ImportError:
12    dbm = None
13    # just some sort of valid exception which might be raised in the
14    # dbm test
15    _dbmerror = IOError
16
17def whichdb(filename):
18    """Guess which db package to use to open a db file.
19
20    Return values:
21
22    - None if the database file can't be read;
23    - empty string if the file can be read but can't be recognized
24    - the module name (e.g. "dbm" or "gdbm") if recognized.
25
26    Importing the given module may still fail, and opening the
27    database using that module may still fail.
28    """
29
30    # Check for dbm first -- this has a .pag and a .dir file
31    try:
32        f = open(filename + os.extsep + "pag", "rb")
33        f.close()
34        # dbm linked with gdbm on OS/2 doesn't have .dir file
35        if not (dbm.library == "GNU gdbm" and sys.platform == "os2emx"):
36            f = open(filename + os.extsep + "dir", "rb")
37            f.close()
38        return "dbm"
39    except IOError:
40        # some dbm emulations based on Berkeley DB generate a .db file
41        # some do not, but they should be caught by the dbhash checks
42        try:
43            f = open(filename + os.extsep + "db", "rb")
44            f.close()
45            # guarantee we can actually open the file using dbm
46            # kind of overkill, but since we are dealing with emulations
47            # it seems like a prudent step
48            if dbm is not None:
49                d = dbm.open(filename)
50                d.close()
51                return "dbm"
52        except (IOError, _dbmerror):
53            pass
54
55    # Check for dumbdbm next -- this has a .dir and a .dat file
56    try:
57        # First check for presence of files
58        os.stat(filename + os.extsep + "dat")
59        size = os.stat(filename + os.extsep + "dir").st_size
60        # dumbdbm files with no keys are empty
61        if size == 0:
62            return "dumbdbm"
63        f = open(filename + os.extsep + "dir", "rb")
64        try:
65            if f.read(1) in ("'", '"'):
66                return "dumbdbm"
67        finally:
68            f.close()
69    except (OSError, IOError):
70        pass
71
72    # See if the file exists, return None if not
73    try:
74        f = open(filename, "rb")
75    except IOError:
76        return None
77
78    # Read the start of the file -- the magic number
79    s16 = f.read(16)
80    f.close()
81    s = s16[0:4]
82
83    # Return "" if not at least 4 bytes
84    if len(s) != 4:
85        return ""
86
87    # Convert to 4-byte int in native byte order -- return "" if impossible
88    try:
89        (magic,) = struct.unpack("=l", s)
90    except struct.error:
91        return ""
92
93    # Check for GNU dbm
94    if magic in (0x13579ace, 0x13579acd, 0x13579acf):
95        return "gdbm"
96
97    # Check for old Berkeley db hash file format v2
98    if magic in (0x00061561, 0x61150600):
99        return "bsddb185"
100
101    # Later versions of Berkeley db hash file have a 12-byte pad in
102    # front of the file type
103    try:
104        (magic,) = struct.unpack("=l", s16[-4:])
105    except struct.error:
106        return ""
107
108    # Check for BSD hash
109    if magic in (0x00061561, 0x61150600):
110        return "dbhash"
111
112    # Unknown
113    return ""
114
115if __name__ == "__main__":
116    for filename in sys.argv[1:]:
117        print whichdb(filename) or "UNKNOWN", filename
118