• 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_RETURN_NONE;
259 }
260 
261 /* XXX Should return a set or a set view */
262 /*[clinic input]
263 _gdbm.gdbm.keys
264 
265 Get a list of all keys in the database.
266 [clinic start generated code]*/
267 
268 static PyObject *
_gdbm_gdbm_keys_impl(dbmobject * self)269 _gdbm_gdbm_keys_impl(dbmobject *self)
270 /*[clinic end generated code: output=cb4b1776c3645dcc input=1832ee0a3132cfaf]*/
271 {
272     PyObject *v, *item;
273     datum key, nextkey;
274     int err;
275 
276     if (self == NULL || !is_dbmobject(self)) {
277         PyErr_BadInternalCall();
278         return NULL;
279     }
280     check_dbmobject_open(self);
281 
282     v = PyList_New(0);
283     if (v == NULL)
284         return NULL;
285 
286     key = gdbm_firstkey(self->di_dbm);
287     while (key.dptr) {
288         item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
289         if (item == NULL) {
290             free(key.dptr);
291             Py_DECREF(v);
292             return NULL;
293         }
294         err = PyList_Append(v, item);
295         Py_DECREF(item);
296         if (err != 0) {
297             free(key.dptr);
298             Py_DECREF(v);
299             return NULL;
300         }
301         nextkey = gdbm_nextkey(self->di_dbm, key);
302         free(key.dptr);
303         key = nextkey;
304     }
305     return v;
306 }
307 
308 static int
dbm_contains(PyObject * self,PyObject * arg)309 dbm_contains(PyObject *self, PyObject *arg)
310 {
311     dbmobject *dp = (dbmobject *)self;
312     datum key;
313     Py_ssize_t size;
314 
315     if ((dp)->di_dbm == NULL) {
316         PyErr_SetString(DbmError,
317                         "GDBM object has already been closed");
318         return -1;
319     }
320     if (PyUnicode_Check(arg)) {
321         key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
322         key.dsize = size;
323         if (key.dptr == NULL)
324             return -1;
325     }
326     else if (!PyBytes_Check(arg)) {
327         PyErr_Format(PyExc_TypeError,
328                      "gdbm key must be bytes or string, not %.100s",
329                      arg->ob_type->tp_name);
330         return -1;
331     }
332     else {
333         key.dptr = PyBytes_AS_STRING(arg);
334         key.dsize = PyBytes_GET_SIZE(arg);
335     }
336     return gdbm_exists(dp->di_dbm, key);
337 }
338 
339 static PySequenceMethods dbm_as_sequence = {
340         0,                      /* sq_length */
341         0,                      /* sq_concat */
342         0,                      /* sq_repeat */
343         0,                      /* sq_item */
344         0,                      /* sq_slice */
345         0,                      /* sq_ass_item */
346         0,                      /* sq_ass_slice */
347         dbm_contains,           /* sq_contains */
348         0,                      /* sq_inplace_concat */
349         0,                      /* sq_inplace_repeat */
350 };
351 
352 /*[clinic input]
353 _gdbm.gdbm.firstkey
354 
355 Return the starting key for the traversal.
356 
357 It's possible to loop over every key in the database using this method
358 and the nextkey() method.  The traversal is ordered by GDBM's internal
359 hash values, and won't be sorted by the key values.
360 [clinic start generated code]*/
361 
362 static PyObject *
_gdbm_gdbm_firstkey_impl(dbmobject * self)363 _gdbm_gdbm_firstkey_impl(dbmobject *self)
364 /*[clinic end generated code: output=9ff85628d84b65d2 input=0dbd6a335d69bba0]*/
365 {
366     PyObject *v;
367     datum key;
368 
369     check_dbmobject_open(self);
370     key = gdbm_firstkey(self->di_dbm);
371     if (key.dptr) {
372         v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
373         free(key.dptr);
374         return v;
375     }
376     else {
377         Py_RETURN_NONE;
378     }
379 }
380 
381 /*[clinic input]
382 _gdbm.gdbm.nextkey
383 
384     key: str(accept={str, robuffer}, zeroes=True)
385     /
386 
387 Returns the key that follows key in the traversal.
388 
389 The following code prints every key in the database db, without having
390 to create a list in memory that contains them all:
391 
392       k = db.firstkey()
393       while k != None:
394           print(k)
395           k = db.nextkey(k)
396 [clinic start generated code]*/
397 
398 static PyObject *
_gdbm_gdbm_nextkey_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length)399 _gdbm_gdbm_nextkey_impl(dbmobject *self, const char *key,
400                         Py_ssize_clean_t key_length)
401 /*[clinic end generated code: output=192ab892de6eb2f6 input=1f1606943614e36f]*/
402 {
403     PyObject *v;
404     datum dbm_key, nextkey;
405 
406     dbm_key.dptr = (char *)key;
407     dbm_key.dsize = key_length;
408     check_dbmobject_open(self);
409     nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
410     if (nextkey.dptr) {
411         v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
412         free(nextkey.dptr);
413         return v;
414     }
415     else {
416         Py_RETURN_NONE;
417     }
418 }
419 
420 /*[clinic input]
421 _gdbm.gdbm.reorganize
422 
423 Reorganize the database.
424 
425 If you have carried out a lot of deletions and would like to shrink
426 the space used by the GDBM file, this routine will reorganize the
427 database.  GDBM will not shorten the length of a database file except
428 by using this reorganization; otherwise, deleted file space will be
429 kept and reused as new (key,value) pairs are added.
430 [clinic start generated code]*/
431 
432 static PyObject *
_gdbm_gdbm_reorganize_impl(dbmobject * self)433 _gdbm_gdbm_reorganize_impl(dbmobject *self)
434 /*[clinic end generated code: output=38d9624df92e961d input=f6bea85bcfd40dd2]*/
435 {
436     check_dbmobject_open(self);
437     errno = 0;
438     if (gdbm_reorganize(self->di_dbm) < 0) {
439         if (errno != 0)
440             PyErr_SetFromErrno(DbmError);
441         else
442             PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
443         return NULL;
444     }
445     Py_RETURN_NONE;
446 }
447 
448 /*[clinic input]
449 _gdbm.gdbm.sync
450 
451 Flush the database to the disk file.
452 
453 When the database has been opened in fast mode, this method forces
454 any unwritten data to be written to the disk.
455 [clinic start generated code]*/
456 
457 static PyObject *
_gdbm_gdbm_sync_impl(dbmobject * self)458 _gdbm_gdbm_sync_impl(dbmobject *self)
459 /*[clinic end generated code: output=488b15f47028f125 input=2a47d2c9e153ab8a]*/
460 {
461     check_dbmobject_open(self);
462     gdbm_sync(self->di_dbm);
463     Py_RETURN_NONE;
464 }
465 
466 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)467 dbm__enter__(PyObject *self, PyObject *args)
468 {
469     Py_INCREF(self);
470     return self;
471 }
472 
473 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)474 dbm__exit__(PyObject *self, PyObject *args)
475 {
476     _Py_IDENTIFIER(close);
477     return _PyObject_CallMethodId(self, &PyId_close, NULL);
478 }
479 
480 static PyMethodDef dbm_methods[] = {
481     _GDBM_GDBM_CLOSE_METHODDEF
482     _GDBM_GDBM_KEYS_METHODDEF
483     _GDBM_GDBM_FIRSTKEY_METHODDEF
484     _GDBM_GDBM_NEXTKEY_METHODDEF
485     _GDBM_GDBM_REORGANIZE_METHODDEF
486     _GDBM_GDBM_SYNC_METHODDEF
487     _GDBM_GDBM_GET_METHODDEF
488     _GDBM_GDBM_GET_METHODDEF
489     _GDBM_GDBM_SETDEFAULT_METHODDEF
490     {"__enter__", dbm__enter__, METH_NOARGS, NULL},
491     {"__exit__",  dbm__exit__, METH_VARARGS, NULL},
492     {NULL,              NULL}           /* sentinel */
493 };
494 
495 static PyTypeObject Dbmtype = {
496     PyVarObject_HEAD_INIT(0, 0)
497     "_gdbm.gdbm",
498     sizeof(dbmobject),
499     0,
500     (destructor)dbm_dealloc,            /*tp_dealloc*/
501     0,                                  /*tp_print*/
502     0,                                  /*tp_getattr*/
503     0,                                  /*tp_setattr*/
504     0,                                  /*tp_reserved*/
505     0,                                  /*tp_repr*/
506     0,                                  /*tp_as_number*/
507     &dbm_as_sequence,                   /*tp_as_sequence*/
508     &dbm_as_mapping,                    /*tp_as_mapping*/
509     0,                                  /*tp_hash*/
510     0,                                  /*tp_call*/
511     0,                                  /*tp_str*/
512     0,                                  /*tp_getattro*/
513     0,                                  /*tp_setattro*/
514     0,                                  /*tp_as_buffer*/
515     Py_TPFLAGS_DEFAULT,                 /*tp_xxx4*/
516     gdbm_object__doc__,                 /*tp_doc*/
517     0,                                  /*tp_traverse*/
518     0,                                  /*tp_clear*/
519     0,                                  /*tp_richcompare*/
520     0,                                  /*tp_weaklistoffset*/
521     0,                                  /*tp_iter*/
522     0,                                  /*tp_iternext*/
523     dbm_methods,                        /*tp_methods*/
524 };
525 
526 /* ----------------------------------------------------------------- */
527 
528 /*[clinic input]
529 _gdbm.open as dbmopen
530     filename: unicode
531     flags: str="r"
532     mode: int(py_default="0o666") = 0o666
533     /
534 
535 Open a dbm database and return a dbm object.
536 
537 The filename argument is the name of the database file.
538 
539 The optional flags argument can be 'r' (to open an existing database
540 for reading only -- default), 'w' (to open an existing database for
541 reading and writing), 'c' (which creates the database if it doesn't
542 exist), or 'n' (which always creates a new empty database).
543 
544 Some versions of gdbm support additional flags which must be
545 appended to one of the flags described above.  The module constant
546 'open_flags' is a string of valid additional flags.  The 'f' flag
547 opens the database in fast mode; altered data will not automatically
548 be written to the disk after every change.  This results in faster
549 writes to the database, but may result in an inconsistent database
550 if the program crashes while the database is still open.  Use the
551 sync() method to force any unwritten data to be written to the disk.
552 The 's' flag causes all database operations to be synchronized to
553 disk.  The 'u' flag disables locking of the database file.
554 
555 The optional mode argument is the Unix mode of the file, used only
556 when the database has to be created.  It defaults to octal 0o666.
557 [clinic start generated code]*/
558 
559 static PyObject *
dbmopen_impl(PyObject * module,PyObject * filename,const char * flags,int mode)560 dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
561              int mode)
562 /*[clinic end generated code: output=9527750f5df90764 input=3be0b0875974b928]*/
563 {
564     int iflags;
565 
566     switch (flags[0]) {
567     case 'r':
568         iflags = GDBM_READER;
569         break;
570     case 'w':
571         iflags = GDBM_WRITER;
572         break;
573     case 'c':
574         iflags = GDBM_WRCREAT;
575         break;
576     case 'n':
577         iflags = GDBM_NEWDB;
578         break;
579     default:
580         PyErr_SetString(DbmError,
581                         "First flag must be one of 'r', 'w', 'c' or 'n'");
582         return NULL;
583     }
584     for (flags++; *flags != '\0'; flags++) {
585         char buf[40];
586         switch (*flags) {
587 #ifdef GDBM_FAST
588             case 'f':
589                 iflags |= GDBM_FAST;
590                 break;
591 #endif
592 #ifdef GDBM_SYNC
593             case 's':
594                 iflags |= GDBM_SYNC;
595                 break;
596 #endif
597 #ifdef GDBM_NOLOCK
598             case 'u':
599                 iflags |= GDBM_NOLOCK;
600                 break;
601 #endif
602             default:
603                 PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
604                               *flags);
605                 PyErr_SetString(DbmError, buf);
606                 return NULL;
607         }
608     }
609 
610     PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename);
611     if (filenamebytes == NULL) {
612         return NULL;
613     }
614     const char *name = PyBytes_AS_STRING(filenamebytes);
615     if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
616         Py_DECREF(filenamebytes);
617         PyErr_SetString(PyExc_ValueError, "embedded null character");
618         return NULL;
619     }
620     PyObject *self = newdbmobject(name, iflags, mode);
621     Py_DECREF(filenamebytes);
622     return self;
623 }
624 
625 static const char dbmmodule_open_flags[] = "rwcn"
626 #ifdef GDBM_FAST
627                                      "f"
628 #endif
629 #ifdef GDBM_SYNC
630                                      "s"
631 #endif
632 #ifdef GDBM_NOLOCK
633                                      "u"
634 #endif
635                                      ;
636 
637 static PyMethodDef dbmmodule_methods[] = {
638     DBMOPEN_METHODDEF
639     { 0, 0 },
640 };
641 
642 
643 static struct PyModuleDef _gdbmmodule = {
644         PyModuleDef_HEAD_INIT,
645         "_gdbm",
646         gdbmmodule__doc__,
647         -1,
648         dbmmodule_methods,
649         NULL,
650         NULL,
651         NULL,
652         NULL
653 };
654 
655 PyMODINIT_FUNC
PyInit__gdbm(void)656 PyInit__gdbm(void) {
657     PyObject *m, *d, *s;
658 
659     if (PyType_Ready(&Dbmtype) < 0)
660             return NULL;
661     m = PyModule_Create(&_gdbmmodule);
662     if (m == NULL)
663         return NULL;
664     d = PyModule_GetDict(m);
665     DbmError = PyErr_NewException("_gdbm.error", PyExc_OSError, NULL);
666     if (DbmError != NULL) {
667         PyDict_SetItemString(d, "error", DbmError);
668         s = PyUnicode_FromString(dbmmodule_open_flags);
669         PyDict_SetItemString(d, "open_flags", s);
670         Py_DECREF(s);
671     }
672     return m;
673 }
674