generic interface to all dbm clones use import dbm d dbm openfile w 0o666 the returned object is a dbm gnu dbm ndbm or dbm dumb object dependent on the type of database being opened determined by the whichdb function in the case of an existing dbm if the dbm does not exist and the create or new flag c or n was specified the dbm type will be determined by the availability of the modules tested in the above order it has the following interface key and data are strings dkey data store data at key may override data at existing key data dkey retrieve data at key raise keyerror if no such key del dkey delete data stored at key raises keyerror if no such key flag key in d true if the key exists list d keys return a list of all existing keys slow future versions may change the order in which implementations are tested for existence and add interfaces to other dbmlike implementations open or create database at path given by file optional argument flag can be r default for readonly access w for readwrite access of an existing database c for readwrite access to a new or existing database and n for readwrite access to a new database note r and w fail if the database doesn t exist c creates it only if it doesn t exist and n always creates a new database guess the type of an existing database if not creating a new one db doesn t exist or n flag was specified to create a new db file doesn t exist and the new flag was used so use default type db type cannot be determined guess which db package to use to open a db file return values none if the database file can t be read empty string if the file can be read but can t be recognized the name of the dbm submodule e g ndbm or gnu if recognized importing the given module may still fail and opening the database using that module may still fail check for ndbm first this has a pag and a dir file some dbm emulations based on berkeley db generate a db file some do not but they should be caught by the bsd checks guarantee we can actually open the file using dbm kind of overkill but since we are dealing with emulations it seems like a prudent step check for dumbdbm next this has a dir and a dat file first check for presence of files dumbdbm files with no keys are empty see if the file exists return none if not read the start of the file the magic number return if not at least 4 bytes convert to 4byte int in native byte order return if impossible check for gnu dbm later versions of berkeley db hash file have a 12byte pad in front of the file type unknown open or create database at path given by file optional argument flag can be r default for read only access w for read write access of an existing database c for read write access to a new or existing database and n for read write access to a new database note r and w fail if the database doesn t exist c creates it only if it doesn t exist and n always creates a new database guess the type of an existing database if not creating a new one db doesn t exist or n flag was specified to create a new db file doesn t exist and the new flag was used so use default type db type cannot be determined guess which db package to use to open a db file return values none if the database file can t be read empty string if the file can be read but can t be recognized the name of the dbm submodule e g ndbm or gnu if recognized importing the given module may still fail and opening the database using that module may still fail check for ndbm first this has a pag and a dir file some dbm emulations based on berkeley db generate a db file some do not but they should be caught by the bsd checks guarantee we can actually open the file using dbm kind of overkill but since we are dealing with emulations it seems like a prudent step check for dumbdbm next this has a dir and a dat file first check for presence of files dumbdbm files with no keys are empty see if the file exists return none if not read the start of the file the magic number return if not at least 4 bytes convert to 4 byte int in native byte order return if impossible check for gnu dbm later versions of berkeley db hash file have a 12 byte pad in front of the file type unknown
__all__ = ['open', 'whichdb', 'error']

import io
import os
import struct
import sys


class error(Exception):
    pass

_names = ['dbm.gnu', 'dbm.ndbm', 'dbm.dumb']
_defaultmod = None
_modules = {}

error = (error, OSError)

try:
    from dbm import ndbm
except ImportError:
    ndbm = None


def open(file, flag='r', mode=0o666):
    
    global _defaultmod
    if _defaultmod is None:
        for name in _names:
            try:
                mod = __import__(name, fromlist=['open'])
            except ImportError:
                continue
            if not _defaultmod:
                _defaultmod = mod
            _modules[name] = mod
        if not _defaultmod:
            raise ImportError("no dbm clone found; tried %s" % _names)

    
    result = whichdb(file) if 'n' not in flag else None
    if result is None:
        
        if 'c' in flag or 'n' in flag:
            
            mod = _defaultmod
        else:
            raise error[0]("db file doesn't exist; "
                           "use 'c' or 'n' flag to create a new db")
    elif result == "":
        
        raise error[0]("db type could not be determined")
    elif result not in _modules:
        raise error[0]("db type is {0}, but the module is not "
                       "available".format(result))
    else:
        mod = _modules[result]
    return mod.open(file, flag, mode)


def whichdb(filename):
    

    
    filename = os.fsencode(filename)
    try:
        f = io.open(filename + b".pag", "rb")
        f.close()
        f = io.open(filename + b".dir", "rb")
        f.close()
        return "dbm.ndbm"
    except OSError:
        
        
        try:
            f = io.open(filename + b".db", "rb")
            f.close()
            
            
            
            if ndbm is not None:
                d = ndbm.open(filename)
                d.close()
                return "dbm.ndbm"
        except OSError:
            pass

    
    try:
        
        os.stat(filename + b".dat")
        size = os.stat(filename + b".dir").st_size
        
        if size == 0:
            return "dbm.dumb"
        f = io.open(filename + b".dir", "rb")
        try:
            if f.read(1) in (b"'", b'"'):
                return "dbm.dumb"
        finally:
            f.close()
    except OSError:
        pass

    
    try:
        f = io.open(filename, "rb")
    except OSError:
        return None

    with f:
        
        s16 = f.read(16)
    s = s16[0:4]

    
    if len(s) != 4:
        return ""

    
    try:
        (magic,) = struct.unpack("=l", s)
    except struct.error:
        return ""

    
    if magic in (0x13579ace, 0x13579acd, 0x13579acf):
        return "dbm.gnu"

    
    
    try:
        (magic,) = struct.unpack("=l", s16[-4:])
    except struct.error:
        return ""

    
    return ""


if __name__ == "__main__":
    for filename in sys.argv[1:]:
        print(whichdb(filename) or "UNKNOWN", filename)