1
2/* DBM module using dictionary interface */
3/* Author: Anthony Baxter, after dbmmodule.c */
4/* Doc strings: Mitch Chapman */
5
6
7#include "Python.h"
8
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <fcntl.h>
12#include "gdbm.h"
13
14#if defined(WIN32) && !defined(__CYGWIN__)
15#include "gdbmerrno.h"
16extern const char * gdbm_strerror(gdbm_error);
17#endif
18
19/*[clinic input]
20module _gdbm
21class _gdbm.gdbm "dbmobject *" "&Dbmtype"
22[clinic start generated code]*/
23/*[clinic end generated code: output=da39a3ee5e6b4b0d input=113927c6170729b2]*/
24
25PyDoc_STRVAR(gdbmmodule__doc__,
26"This module provides an interface to the GNU DBM (GDBM) library.\n\
27\n\
28This module is quite similar to the dbm module, but uses GDBM instead to\n\
29provide some additional functionality.  Please note that the file formats\n\
30created by GDBM and dbm are incompatible.\n\
31\n\
32GDBM objects behave like mappings (dictionaries), except that keys and\n\
33values are always immutable bytes-like objects or strings.  Printing\n\
34a GDBM object doesn't print the keys and values, and the items() and\n\
35values() methods are not supported.");
36
37typedef struct {
38    PyObject_HEAD
39    int di_size;        /* -1 means recompute */
40    GDBM_FILE di_dbm;
41} dbmobject;
42
43static PyTypeObject Dbmtype;
44
45#include "clinic/_gdbmmodule.c.h"
46
47#define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype)
48#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
49    { PyErr_SetString(DbmError, "GDBM object has already been closed"); \
50      return NULL; }
51
52
53
54static PyObject *DbmError;
55
56PyDoc_STRVAR(gdbm_object__doc__,
57"This object represents a GDBM database.\n\
58GDBM objects behave like mappings (dictionaries), except that keys and\n\
59values are always immutable bytes-like objects or strings.  Printing\n\
60a GDBM object doesn't print the keys and values, and the items() and\n\
61values() methods are not supported.\n\
62\n\
63GDBM objects also support additional operations such as firstkey,\n\
64nextkey, reorganize, and sync.");
65
66static PyObject *
67newdbmobject(const char *file, int flags, int mode)
68{
69    dbmobject *dp;
70
71    dp = PyObject_New(dbmobject, &Dbmtype);
72    if (dp == NULL)
73        return NULL;
74    dp->di_size = -1;
75    errno = 0;
76    if ((dp->di_dbm = gdbm_open((char *)file, 0, flags, mode, NULL)) == 0) {
77        if (errno != 0)
78            PyErr_SetFromErrno(DbmError);
79        else
80            PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
81        Py_DECREF(dp);
82        return NULL;
83    }
84    return (PyObject *)dp;
85}
86
87/* Methods */
88
89static void
90dbm_dealloc(dbmobject *dp)
91{
92    if (dp->di_dbm)
93        gdbm_close(dp->di_dbm);
94    PyObject_Del(dp);
95}
96
97static Py_ssize_t
98dbm_length(dbmobject *dp)
99{
100    if (dp->di_dbm == NULL) {
101        PyErr_SetString(DbmError, "GDBM object has already been closed");
102        return -1;
103    }
104    if (dp->di_size < 0) {
105        datum key,okey;
106        int size;
107        okey.dsize=0;
108        okey.dptr=NULL;
109
110        size = 0;
111        for (key=gdbm_firstkey(dp->di_dbm); key.dptr;
112             key = gdbm_nextkey(dp->di_dbm,okey)) {
113            size++;
114            if(okey.dsize) free(okey.dptr);
115            okey=key;
116        }
117        dp->di_size = size;
118    }
119    return dp->di_size;
120}
121
122static PyObject *
123dbm_subscript(dbmobject *dp, PyObject *key)
124{
125    PyObject *v;
126    datum drec, krec;
127
128    if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) )
129        return NULL;
130
131    if (dp->di_dbm == NULL) {
132        PyErr_SetString(DbmError,
133                        "GDBM object has already been closed");
134        return NULL;
135    }
136    drec = gdbm_fetch(dp->di_dbm, krec);
137    if (drec.dptr == 0) {
138        PyErr_SetObject(PyExc_KeyError, key);
139        return NULL;
140    }
141    v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
142    free(drec.dptr);
143    return v;
144}
145
146/*[clinic input]
147_gdbm.gdbm.get
148
149    key: object
150    default: object = None
151    /
152
153Get the value for key, or default if not present.
154[clinic start generated code]*/
155
156static PyObject *
157_gdbm_gdbm_get_impl(dbmobject *self, PyObject *key, PyObject *default_value)
158/*[clinic end generated code: output=19b7c585ad4f554a input=a9c20423f34c17b6]*/
159{
160    PyObject *res;
161
162    res = dbm_subscript(self, key);
163    if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
164        PyErr_Clear();
165        Py_INCREF(default_value);
166        return default_value;
167    }
168    return res;
169}
170
171static int
172dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
173{
174    datum krec, drec;
175
176    if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) {
177        PyErr_SetString(PyExc_TypeError,
178                        "gdbm mappings have bytes or string indices only");
179        return -1;
180    }
181    if (dp->di_dbm == NULL) {
182        PyErr_SetString(DbmError,
183                        "GDBM object has already been closed");
184        return -1;
185    }
186    dp->di_size = -1;
187    if (w == NULL) {
188        if (gdbm_delete(dp->di_dbm, krec) < 0) {
189            PyErr_SetObject(PyExc_KeyError, v);
190            return -1;
191        }
192    }
193    else {
194        if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) {
195            PyErr_SetString(PyExc_TypeError,
196                            "gdbm mappings have byte or string elements only");
197            return -1;
198        }
199        errno = 0;
200        if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
201            if (errno != 0)
202                PyErr_SetFromErrno(DbmError);
203            else
204                PyErr_SetString(DbmError,
205                                gdbm_strerror(gdbm_errno));
206            return -1;
207        }
208    }
209    return 0;
210}
211
212/*[clinic input]
213_gdbm.gdbm.setdefault
214
215    key: object
216    default: object = None
217    /
218
219Get value for key, or set it to default and return default if not present.
220[clinic start generated code]*/
221
222static PyObject *
223_gdbm_gdbm_setdefault_impl(dbmobject *self, PyObject *key,
224                           PyObject *default_value)
225/*[clinic end generated code: output=88760ee520329012 input=0db46b69e9680171]*/
226{
227    PyObject *res;
228
229    res = dbm_subscript(self, key);
230    if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
231        PyErr_Clear();
232        if (dbm_ass_sub(self, key, default_value) < 0)
233            return NULL;
234        return dbm_subscript(self, key);
235    }
236    return res;
237}
238
239static PyMappingMethods dbm_as_mapping = {
240    (lenfunc)dbm_length,                /*mp_length*/
241    (binaryfunc)dbm_subscript,          /*mp_subscript*/
242    (objobjargproc)dbm_ass_sub,         /*mp_ass_subscript*/
243};
244
245/*[clinic input]
246_gdbm.gdbm.close
247
248Close the database.
249[clinic start generated code]*/
250
251static PyObject *
252_gdbm_gdbm_close_impl(dbmobject *self)
253/*[clinic end generated code: output=23512a594598b563 input=0a203447379b45fd]*/
254{
255    if (self->di_dbm)
256        gdbm_close(self->di_dbm);
257    self->di_dbm = NULL;
258    Py_INCREF(Py_None);
259    return Py_None;
260}
261
262/* XXX Should return a set or a set view */
263/*[clinic input]
264_gdbm.gdbm.keys
265
266Get a list of all keys in the database.
267[clinic start generated code]*/
268
269static PyObject *
270_gdbm_gdbm_keys_impl(dbmobject *self)
271/*[clinic end generated code: output=cb4b1776c3645dcc input=1832ee0a3132cfaf]*/
272{
273    PyObject *v, *item;
274    datum key, nextkey;
275    int err;
276
277    if (self == NULL || !is_dbmobject(self)) {
278        PyErr_BadInternalCall();
279        return NULL;
280    }
281    check_dbmobject_open(self);
282
283    v = PyList_New(0);
284    if (v == NULL)
285        return NULL;
286
287    key = gdbm_firstkey(self->di_dbm);
288    while (key.dptr) {
289        item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
290        if (item == NULL) {
291            free(key.dptr);
292            Py_DECREF(v);
293            return NULL;
294        }
295        err = PyList_Append(v, item);
296        Py_DECREF(item);
297        if (err != 0) {
298            free(key.dptr);
299            Py_DECREF(v);
300            return NULL;
301        }
302        nextkey = gdbm_nextkey(self->di_dbm, key);
303        free(key.dptr);
304        key = nextkey;
305    }
306    return v;
307}
308
309static int
310dbm_contains(PyObject *self, PyObject *arg)
311{
312    dbmobject *dp = (dbmobject *)self;
313    datum key;
314    Py_ssize_t size;
315
316    if ((dp)->di_dbm == NULL) {
317        PyErr_SetString(DbmError,
318                        "GDBM object has already been closed");
319        return -1;
320    }
321    if (PyUnicode_Check(arg)) {
322        key.dptr = PyUnicode_AsUTF8AndSize(arg, &size);
323        key.dsize = size;
324        if (key.dptr == NULL)
325            return -1;
326    }
327    else if (!PyBytes_Check(arg)) {
328        PyErr_Format(PyExc_TypeError,
329                     "gdbm key must be bytes or string, not %.100s",
330                     arg->ob_type->tp_name);
331        return -1;
332    }
333    else {
334        key.dptr = PyBytes_AS_STRING(arg);
335        key.dsize = PyBytes_GET_SIZE(arg);
336    }
337    return gdbm_exists(dp->di_dbm, key);
338}
339
340static PySequenceMethods dbm_as_sequence = {
341        0,                      /* sq_length */
342        0,                      /* sq_concat */
343        0,                      /* sq_repeat */
344        0,                      /* sq_item */
345        0,                      /* sq_slice */
346        0,                      /* sq_ass_item */
347        0,                      /* sq_ass_slice */
348        dbm_contains,           /* sq_contains */
349        0,                      /* sq_inplace_concat */
350        0,                      /* sq_inplace_repeat */
351};
352
353/*[clinic input]
354_gdbm.gdbm.firstkey
355
356Return the starting key for the traversal.
357
358It's possible to loop over every key in the database using this method
359and the nextkey() method.  The traversal is ordered by GDBM's internal
360hash values, and won't be sorted by the key values.
361[clinic start generated code]*/
362
363static PyObject *
364_gdbm_gdbm_firstkey_impl(dbmobject *self)
365/*[clinic end generated code: output=9ff85628d84b65d2 input=0dbd6a335d69bba0]*/
366{
367    PyObject *v;
368    datum key;
369
370    check_dbmobject_open(self);
371    key = gdbm_firstkey(self->di_dbm);
372    if (key.dptr) {
373        v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
374        free(key.dptr);
375        return v;
376    }
377    else {
378        Py_INCREF(Py_None);
379        return Py_None;
380    }
381}
382
383/*[clinic input]
384_gdbm.gdbm.nextkey
385
386    key: str(accept={str, robuffer}, zeroes=True)
387    /
388
389Returns the key that follows key in the traversal.
390
391The following code prints every key in the database db, without having
392to create a list in memory that contains them all:
393
394      k = db.firstkey()
395      while k != None:
396          print(k)
397          k = db.nextkey(k)
398[clinic start generated code]*/
399
400static PyObject *
401_gdbm_gdbm_nextkey_impl(dbmobject *self, const char *key,
402                        Py_ssize_clean_t key_length)
403/*[clinic end generated code: output=192ab892de6eb2f6 input=1f1606943614e36f]*/
404{
405    PyObject *v;
406    datum dbm_key, nextkey;
407
408    dbm_key.dptr = (char *)key;
409    dbm_key.dsize = key_length;
410    check_dbmobject_open(self);
411    nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
412    if (nextkey.dptr) {
413        v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
414        free(nextkey.dptr);
415        return v;
416    }
417    else {
418        Py_INCREF(Py_None);
419        return Py_None;
420    }
421}
422
423/*[clinic input]
424_gdbm.gdbm.reorganize
425
426Reorganize the database.
427
428If you have carried out a lot of deletions and would like to shrink
429the space used by the GDBM file, this routine will reorganize the
430database.  GDBM will not shorten the length of a database file except
431by using this reorganization; otherwise, deleted file space will be
432kept and reused as new (key,value) pairs are added.
433[clinic start generated code]*/
434
435static PyObject *
436_gdbm_gdbm_reorganize_impl(dbmobject *self)
437/*[clinic end generated code: output=38d9624df92e961d input=f6bea85bcfd40dd2]*/
438{
439    check_dbmobject_open(self);
440    errno = 0;
441    if (gdbm_reorganize(self->di_dbm) < 0) {
442        if (errno != 0)
443            PyErr_SetFromErrno(DbmError);
444        else
445            PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
446        return NULL;
447    }
448    Py_INCREF(Py_None);
449    return Py_None;
450}
451
452/*[clinic input]
453_gdbm.gdbm.sync
454
455Flush the database to the disk file.
456
457When the database has been opened in fast mode, this method forces
458any unwritten data to be written to the disk.
459[clinic start generated code]*/
460
461static PyObject *
462_gdbm_gdbm_sync_impl(dbmobject *self)
463/*[clinic end generated code: output=488b15f47028f125 input=2a47d2c9e153ab8a]*/
464{
465    check_dbmobject_open(self);
466    gdbm_sync(self->di_dbm);
467    Py_INCREF(Py_None);
468    return Py_None;
469}
470
471static PyObject *
472dbm__enter__(PyObject *self, PyObject *args)
473{
474    Py_INCREF(self);
475    return self;
476}
477
478static PyObject *
479dbm__exit__(PyObject *self, PyObject *args)
480{
481    _Py_IDENTIFIER(close);
482    return _PyObject_CallMethodId(self, &PyId_close, NULL);
483}
484
485static PyMethodDef dbm_methods[] = {
486    _GDBM_GDBM_CLOSE_METHODDEF
487    _GDBM_GDBM_KEYS_METHODDEF
488    _GDBM_GDBM_FIRSTKEY_METHODDEF
489    _GDBM_GDBM_NEXTKEY_METHODDEF
490    _GDBM_GDBM_REORGANIZE_METHODDEF
491    _GDBM_GDBM_SYNC_METHODDEF
492    _GDBM_GDBM_GET_METHODDEF
493    _GDBM_GDBM_GET_METHODDEF
494    _GDBM_GDBM_SETDEFAULT_METHODDEF
495    {"__enter__", dbm__enter__, METH_NOARGS, NULL},
496    {"__exit__",  dbm__exit__, METH_VARARGS, NULL},
497    {NULL,              NULL}           /* sentinel */
498};
499
500static PyTypeObject Dbmtype = {
501    PyVarObject_HEAD_INIT(0, 0)
502    "_gdbm.gdbm",
503    sizeof(dbmobject),
504    0,
505    (destructor)dbm_dealloc,            /*tp_dealloc*/
506    0,                                  /*tp_print*/
507    0,                                  /*tp_getattr*/
508    0,                                  /*tp_setattr*/
509    0,                                  /*tp_reserved*/
510    0,                                  /*tp_repr*/
511    0,                                  /*tp_as_number*/
512    &dbm_as_sequence,                   /*tp_as_sequence*/
513    &dbm_as_mapping,                    /*tp_as_mapping*/
514    0,                                  /*tp_hash*/
515    0,                                  /*tp_call*/
516    0,                                  /*tp_str*/
517    0,                                  /*tp_getattro*/
518    0,                                  /*tp_setattro*/
519    0,                                  /*tp_as_buffer*/
520    Py_TPFLAGS_DEFAULT,                 /*tp_xxx4*/
521    gdbm_object__doc__,                 /*tp_doc*/
522    0,                                  /*tp_traverse*/
523    0,                                  /*tp_clear*/
524    0,                                  /*tp_richcompare*/
525    0,                                  /*tp_weaklistoffset*/
526    0,                                  /*tp_iter*/
527    0,                                  /*tp_iternext*/
528    dbm_methods,                        /*tp_methods*/
529};
530
531/* ----------------------------------------------------------------- */
532
533/*[clinic input]
534_gdbm.open as dbmopen
535    filename as name: str
536    flags: str="r"
537    mode: int(py_default="0o666") = 0o666
538    /
539
540Open a dbm database and return a dbm object.
541
542The filename argument is the name of the database file.
543
544The optional flags argument can be 'r' (to open an existing database
545for reading only -- default), 'w' (to open an existing database for
546reading and writing), 'c' (which creates the database if it doesn't
547exist), or 'n' (which always creates a new empty database).
548
549Some versions of gdbm support additional flags which must be
550appended to one of the flags described above.  The module constant
551'open_flags' is a string of valid additional flags.  The 'f' flag
552opens the database in fast mode; altered data will not automatically
553be written to the disk after every change.  This results in faster
554writes to the database, but may result in an inconsistent database
555if the program crashes while the database is still open.  Use the
556sync() method to force any unwritten data to be written to the disk.
557The 's' flag causes all database operations to be synchronized to
558disk.  The 'u' flag disables locking of the database file.
559
560The optional mode argument is the Unix mode of the file, used only
561when the database has to be created.  It defaults to octal 0o666.
562[clinic start generated code]*/
563
564static PyObject *
565dbmopen_impl(PyObject *module, const char *name, const char *flags, int mode)
566/*[clinic end generated code: output=31aa1bafdf5da688 input=55563cd60e51984a]*/
567{
568    int iflags;
569
570    switch (flags[0]) {
571    case 'r':
572        iflags = GDBM_READER;
573        break;
574    case 'w':
575        iflags = GDBM_WRITER;
576        break;
577    case 'c':
578        iflags = GDBM_WRCREAT;
579        break;
580    case 'n':
581        iflags = GDBM_NEWDB;
582        break;
583    default:
584        PyErr_SetString(DbmError,
585                        "First flag must be one of 'r', 'w', 'c' or 'n'");
586        return NULL;
587    }
588    for (flags++; *flags != '\0'; flags++) {
589        char buf[40];
590        switch (*flags) {
591#ifdef GDBM_FAST
592            case 'f':
593                iflags |= GDBM_FAST;
594                break;
595#endif
596#ifdef GDBM_SYNC
597            case 's':
598                iflags |= GDBM_SYNC;
599                break;
600#endif
601#ifdef GDBM_NOLOCK
602            case 'u':
603                iflags |= GDBM_NOLOCK;
604                break;
605#endif
606            default:
607                PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
608                              *flags);
609                PyErr_SetString(DbmError, buf);
610                return NULL;
611        }
612    }
613
614    return newdbmobject(name, iflags, mode);
615}
616
617static const char dbmmodule_open_flags[] = "rwcn"
618#ifdef GDBM_FAST
619                                     "f"
620#endif
621#ifdef GDBM_SYNC
622                                     "s"
623#endif
624#ifdef GDBM_NOLOCK
625                                     "u"
626#endif
627                                     ;
628
629static PyMethodDef dbmmodule_methods[] = {
630    DBMOPEN_METHODDEF
631    { 0, 0 },
632};
633
634
635static struct PyModuleDef _gdbmmodule = {
636        PyModuleDef_HEAD_INIT,
637        "_gdbm",
638        gdbmmodule__doc__,
639        -1,
640        dbmmodule_methods,
641        NULL,
642        NULL,
643        NULL,
644        NULL
645};
646
647PyMODINIT_FUNC
648PyInit__gdbm(void) {
649    PyObject *m, *d, *s;
650
651    if (PyType_Ready(&Dbmtype) < 0)
652            return NULL;
653    m = PyModule_Create(&_gdbmmodule);
654    if (m == NULL)
655        return NULL;
656    d = PyModule_GetDict(m);
657    DbmError = PyErr_NewException("_gdbm.error", PyExc_IOError, NULL);
658    if (DbmError != NULL) {
659        PyDict_SetItemString(d, "error", DbmError);
660        s = PyUnicode_FromString(dbmmodule_open_flags);
661        PyDict_SetItemString(d, "open_flags", s);
662        Py_DECREF(s);
663    }
664    return m;
665}
666