• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"
16 extern const char * gdbm_strerror(gdbm_error);
17 #endif
18 
19 /*[clinic input]
20 module _gdbm
21 class _gdbm.gdbm "dbmobject *" "&Dbmtype"
22 [clinic start generated code]*/
23 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=113927c6170729b2]*/
24 
25 PyDoc_STRVAR(gdbmmodule__doc__,
26 "This module provides an interface to the GNU DBM (GDBM) library.\n\
27 \n\
28 This module is quite similar to the dbm module, but uses GDBM instead to\n\
29 provide some additional functionality.  Please note that the file formats\n\
30 created by GDBM and dbm are incompatible.\n\
31 \n\
32 GDBM objects behave like mappings (dictionaries), except that keys and\n\
33 values are always immutable bytes-like objects or strings.  Printing\n\
34 a GDBM object doesn't print the keys and values, and the items() and\n\
35 values() methods are not supported.");
36 
37 typedef struct {
38     PyObject_HEAD
39     int di_size;        /* -1 means recompute */
40     GDBM_FILE di_dbm;
41 } dbmobject;
42 
43 static 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 
54 static PyObject *DbmError;
55 
56 PyDoc_STRVAR(gdbm_object__doc__,
57 "This object represents a GDBM database.\n\
58 GDBM objects behave like mappings (dictionaries), except that keys and\n\
59 values are always immutable bytes-like objects or strings.  Printing\n\
60 a GDBM object doesn't print the keys and values, and the items() and\n\
61 values() methods are not supported.\n\
62 \n\
63 GDBM objects also support additional operations such as firstkey,\n\
64 nextkey, reorganize, and sync.");
65 
66 static PyObject *
newdbmobject(const char * file,int flags,int mode)67 newdbmobject(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 
89 static void
dbm_dealloc(dbmobject * dp)90 dbm_dealloc(dbmobject *dp)
91 {
92     if (dp->di_dbm)
93         gdbm_close(dp->di_dbm);
94     PyObject_Del(dp);
95 }
96 
97 static Py_ssize_t
dbm_length(dbmobject * dp)98 dbm_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 
122 static PyObject *
dbm_subscript(dbmobject * dp,PyObject * key)123 dbm_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 
153 Get the value for key, or default if not present.
154 [clinic start generated code]*/
155 
156 static PyObject *
_gdbm_gdbm_get_impl(dbmobject * self,PyObject * key,PyObject * default_value)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 
171 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)172 dbm_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 
219 Get value for key, or set it to default and return default if not present.
220 [clinic start generated code]*/
221 
222 static PyObject *
_gdbm_gdbm_setdefault_impl(dbmobject * self,PyObject * key,PyObject * default_value)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 
239 static 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 
248 Close the database.
249 [clinic start generated code]*/
250 
251 static PyObject *
_gdbm_gdbm_close_impl(dbmobject * self)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 
266 Get a list of all keys in the database.
267 [clinic start generated code]*/
268 
269 static PyObject *
_gdbm_gdbm_keys_impl(dbmobject * self)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 
309 static int
dbm_contains(PyObject * self,PyObject * arg)310 dbm_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 
340 static 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 
356 Return the starting key for the traversal.
357 
358 It's possible to loop over every key in the database using this method
359 and the nextkey() method.  The traversal is ordered by GDBM's internal
360 hash values, and won't be sorted by the key values.
361 [clinic start generated code]*/
362 
363 static PyObject *
_gdbm_gdbm_firstkey_impl(dbmobject * self)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 
389 Returns the key that follows key in the traversal.
390 
391 The following code prints every key in the database db, without having
392 to 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 
400 static PyObject *
_gdbm_gdbm_nextkey_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length)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 
426 Reorganize the database.
427 
428 If you have carried out a lot of deletions and would like to shrink
429 the space used by the GDBM file, this routine will reorganize the
430 database.  GDBM will not shorten the length of a database file except
431 by using this reorganization; otherwise, deleted file space will be
432 kept and reused as new (key,value) pairs are added.
433 [clinic start generated code]*/
434 
435 static PyObject *
_gdbm_gdbm_reorganize_impl(dbmobject * self)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 
455 Flush the database to the disk file.
456 
457 When the database has been opened in fast mode, this method forces
458 any unwritten data to be written to the disk.
459 [clinic start generated code]*/
460 
461 static PyObject *
_gdbm_gdbm_sync_impl(dbmobject * self)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 
471 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)472 dbm__enter__(PyObject *self, PyObject *args)
473 {
474     Py_INCREF(self);
475     return self;
476 }
477 
478 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)479 dbm__exit__(PyObject *self, PyObject *args)
480 {
481     _Py_IDENTIFIER(close);
482     return _PyObject_CallMethodId(self, &PyId_close, NULL);
483 }
484 
485 static 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 
500 static 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 
540 Open a dbm database and return a dbm object.
541 
542 The filename argument is the name of the database file.
543 
544 The optional flags argument can be 'r' (to open an existing database
545 for reading only -- default), 'w' (to open an existing database for
546 reading and writing), 'c' (which creates the database if it doesn't
547 exist), or 'n' (which always creates a new empty database).
548 
549 Some versions of gdbm support additional flags which must be
550 appended to one of the flags described above.  The module constant
551 'open_flags' is a string of valid additional flags.  The 'f' flag
552 opens the database in fast mode; altered data will not automatically
553 be written to the disk after every change.  This results in faster
554 writes to the database, but may result in an inconsistent database
555 if the program crashes while the database is still open.  Use the
556 sync() method to force any unwritten data to be written to the disk.
557 The 's' flag causes all database operations to be synchronized to
558 disk.  The 'u' flag disables locking of the database file.
559 
560 The optional mode argument is the Unix mode of the file, used only
561 when the database has to be created.  It defaults to octal 0o666.
562 [clinic start generated code]*/
563 
564 static PyObject *
dbmopen_impl(PyObject * module,const char * name,const char * flags,int mode)565 dbmopen_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 
617 static 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 
629 static PyMethodDef dbmmodule_methods[] = {
630     DBMOPEN_METHODDEF
631     { 0, 0 },
632 };
633 
634 
635 static 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 
647 PyMODINIT_FUNC
PyInit__gdbm(void)648 PyInit__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