183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# !/usr/bin/env python
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""Guess which db package to use to open a db file."""
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport os
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport struct
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport sys
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import dbm
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _dbmerror = dbm.error
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dbm = None
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # just some sort of valid exception which might be raised in the
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # dbm test
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _dbmerror = IOError
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef whichdb(filename):
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Guess which db package to use to open a db file.
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Return values:
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    - None if the database file can't be read;
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    - empty string if the file can be read but can't be recognized
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    - the module name (e.g. "dbm" or "gdbm") if recognized.
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Importing the given module may still fail, and opening the
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    database using that module may still fail.
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Check for dbm first -- this has a .pag and a .dir file
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = open(filename + os.extsep + "pag", "rb")
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # dbm linked with gdbm on OS/2 doesn't have .dir file
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not (dbm.library == "GNU gdbm" and sys.platform == "os2emx"):
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = open(filename + os.extsep + "dir", "rb")
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.close()
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "dbm"
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except IOError:
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # some dbm emulations based on Berkeley DB generate a .db file
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # some do not, but they should be caught by the dbhash checks
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = open(filename + os.extsep + "db", "rb")
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.close()
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # guarantee we can actually open the file using dbm
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # kind of overkill, but since we are dealing with emulations
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # it seems like a prudent step
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if dbm is not None:
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                d = dbm.open(filename)
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                d.close()
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return "dbm"
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except (IOError, _dbmerror):
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Check for dumbdbm next -- this has a .dir and a .dat file
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # First check for presence of files
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        os.stat(filename + os.extsep + "dat")
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        size = os.stat(filename + os.extsep + "dir").st_size
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # dumbdbm files with no keys are empty
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if size == 0:
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return "dumbdbm"
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = open(filename + os.extsep + "dir", "rb")
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if f.read(1) in ("'", '"'):
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return "dumbdbm"
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        finally:
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.close()
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except (OSError, IOError):
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        pass
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # See if the file exists, return None if not
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = open(filename, "rb")
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except IOError:
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return None
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Read the start of the file -- the magic number
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s16 = f.read(16)
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.close()
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = s16[0:4]
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Return "" if not at least 4 bytes
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if len(s) != 4:
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ""
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Convert to 4-byte int in native byte order -- return "" if impossible
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        (magic,) = struct.unpack("=l", s)
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ""
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Check for GNU dbm
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if magic in (0x13579ace, 0x13579acd, 0x13579acf):
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "gdbm"
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Check for old Berkeley db hash file format v2
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if magic in (0x00061561, 0x61150600):
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "bsddb185"
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Later versions of Berkeley db hash file have a 12-byte pad in
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # front of the file type
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        (magic,) = struct.unpack("=l", s16[-4:])
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return ""
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Check for BSD hash
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if magic in (0x00061561, 0x61150600):
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "dbhash"
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Unknown
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return ""
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == "__main__":
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    for filename in sys.argv[1:]:
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print whichdb(filename) or "UNKNOWN", filename
118