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 PyDoc_STRVAR(gdbmmodule__doc__,
20 "This module provides an interface to the GNU DBM (GDBM) library.\n\
21 \n\
22 This module is quite similar to the dbm module, but uses GDBM instead to\n\
23 provide some additional functionality. Please note that the file formats\n\
24 created by GDBM and dbm are incompatible. \n\
25 \n\
26 GDBM objects behave like mappings (dictionaries), except that keys and\n\
27 values are always strings. Printing a GDBM object doesn't print the\n\
28 keys and values, and the items() and values() methods are not\n\
29 supported.");
30 
31 typedef struct {
32     PyObject_HEAD
33     int di_size;	/* -1 means recompute */
34     GDBM_FILE di_dbm;
35 } dbmobject;
36 
37 static PyTypeObject Dbmtype;
38 
39 #define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype)
40 #define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
41     { PyErr_SetString(DbmError, "GDBM object has already been closed"); \
42       return NULL; }
43 
44 
45 
46 static PyObject *DbmError;
47 
48 PyDoc_STRVAR(gdbm_object__doc__,
49 "This object represents a GDBM database.\n\
50 GDBM objects behave like mappings (dictionaries), except that keys and\n\
51 values are always strings. Printing a GDBM object doesn't print the\n\
52 keys and values, and the items() and values() methods are not\n\
53 supported.\n\
54 \n\
55 GDBM objects also support additional operations such as firstkey,\n\
56 nextkey, reorganize, and sync.");
57 
58 static PyObject *
newdbmobject(char * file,int flags,int mode)59 newdbmobject(char *file, int flags, int mode)
60 {
61     dbmobject *dp;
62 
63     dp = PyObject_New(dbmobject, &Dbmtype);
64     if (dp == NULL)
65         return NULL;
66     dp->di_size = -1;
67     errno = 0;
68     if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) {
69         if (errno != 0)
70             PyErr_SetFromErrno(DbmError);
71         else
72             PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
73         Py_DECREF(dp);
74         return NULL;
75     }
76     return (PyObject *)dp;
77 }
78 
79 /* Methods */
80 
81 static void
dbm_dealloc(register dbmobject * dp)82 dbm_dealloc(register dbmobject *dp)
83 {
84     if (dp->di_dbm)
85         gdbm_close(dp->di_dbm);
86     PyObject_Del(dp);
87 }
88 
89 static Py_ssize_t
dbm_length(dbmobject * dp)90 dbm_length(dbmobject *dp)
91 {
92     if (dp->di_dbm == NULL) {
93         PyErr_SetString(DbmError, "GDBM object has already been closed");
94         return -1;
95     }
96     if (dp->di_size < 0) {
97         datum key,okey;
98         int size;
99         okey.dsize=0;
100         okey.dptr=NULL;
101 
102         size = 0;
103         for (key=gdbm_firstkey(dp->di_dbm); key.dptr;
104              key = gdbm_nextkey(dp->di_dbm,okey)) {
105             size++;
106             if(okey.dsize) free(okey.dptr);
107             okey=key;
108         }
109         dp->di_size = size;
110     }
111     return dp->di_size;
112 }
113 
114 static PyObject *
dbm_subscript(dbmobject * dp,register PyObject * key)115 dbm_subscript(dbmobject *dp, register PyObject *key)
116 {
117     PyObject *v;
118     datum drec, krec;
119 
120     if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) )
121         return NULL;
122 
123     if (dp->di_dbm == NULL) {
124         PyErr_SetString(DbmError,
125                         "GDBM object has already been closed");
126         return NULL;
127     }
128     drec = gdbm_fetch(dp->di_dbm, krec);
129     if (drec.dptr == 0) {
130         PyErr_SetString(PyExc_KeyError,
131                         PyString_AS_STRING((PyStringObject *)key));
132         return NULL;
133     }
134     v = PyString_FromStringAndSize(drec.dptr, drec.dsize);
135     free(drec.dptr);
136     return v;
137 }
138 
139 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)140 dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
141 {
142     datum krec, drec;
143 
144     if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) {
145         PyErr_SetString(PyExc_TypeError,
146                         "gdbm mappings have string indices only");
147         return -1;
148     }
149     if (dp->di_dbm == NULL) {
150         PyErr_SetString(DbmError,
151                         "GDBM object has already been closed");
152         return -1;
153     }
154     dp->di_size = -1;
155     if (w == NULL) {
156         if (gdbm_delete(dp->di_dbm, krec) < 0) {
157             PyErr_SetString(PyExc_KeyError,
158                             PyString_AS_STRING((PyStringObject *)v));
159             return -1;
160         }
161     }
162     else {
163         if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) {
164             PyErr_SetString(PyExc_TypeError,
165                             "gdbm mappings have string elements only");
166             return -1;
167         }
168         errno = 0;
169         if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
170             if (errno != 0)
171                 PyErr_SetFromErrno(DbmError);
172             else
173                 PyErr_SetString(DbmError,
174                                 gdbm_strerror(gdbm_errno));
175             return -1;
176         }
177     }
178     return 0;
179 }
180 
181 static int
dbm_contains(register dbmobject * dp,PyObject * arg)182 dbm_contains(register dbmobject *dp, PyObject *arg)
183 {
184     datum key;
185 
186     if ((dp)->di_dbm == NULL) {
187         PyErr_SetString(DbmError,
188                         "GDBM object has already been closed");
189         return -1;
190     }
191     if (!PyString_Check(arg)) {
192         PyErr_Format(PyExc_TypeError,
193                      "gdbm key must be string, not %.100s",
194                      arg->ob_type->tp_name);
195         return -1;
196     }
197     key.dptr = PyString_AS_STRING(arg);
198     key.dsize = PyString_GET_SIZE(arg);
199     return gdbm_exists(dp->di_dbm, key);
200 }
201 
202 static PySequenceMethods dbm_as_sequence = {
203     (lenfunc)dbm_length,               /*_length*/
204        0,              /*sq_concat*/
205        0,                      /*sq_repeat*/
206        0,                      /*sq_item*/
207        0,                      /*sq_slice*/
208        0,                      /*sq_ass_item*/
209        0,                  /*sq_ass_slice*/
210        (objobjproc)dbm_contains,               /*sq_contains*/
211        0,                  /*sq_inplace_concat*/
212        0                   /*sq_inplace_repeat*/
213 };
214 
215 static PyMappingMethods dbm_as_mapping = {
216     (lenfunc)dbm_length,		/*mp_length*/
217     (binaryfunc)dbm_subscript,          /*mp_subscript*/
218     (objobjargproc)dbm_ass_sub,         /*mp_ass_subscript*/
219 };
220 
221 PyDoc_STRVAR(dbm_close__doc__,
222 "close() -> None\n\
223 Closes the database.");
224 
225 static PyObject *
dbm_close(register dbmobject * dp,PyObject * unused)226 dbm_close(register dbmobject *dp, PyObject *unused)
227 {
228     if (dp->di_dbm)
229         gdbm_close(dp->di_dbm);
230     dp->di_dbm = NULL;
231     Py_INCREF(Py_None);
232     return Py_None;
233 }
234 
235 PyDoc_STRVAR(dbm_keys__doc__,
236 "keys() -> list_of_keys\n\
237 Get a list of all keys in the database.");
238 
239 static PyObject *
dbm_keys(register dbmobject * dp,PyObject * unused)240 dbm_keys(register dbmobject *dp, PyObject *unused)
241 {
242     register PyObject *v, *item;
243     datum key, nextkey;
244     int err;
245 
246     if (dp == NULL || !is_dbmobject(dp)) {
247         PyErr_BadInternalCall();
248         return NULL;
249     }
250     check_dbmobject_open(dp);
251 
252     v = PyList_New(0);
253     if (v == NULL)
254         return NULL;
255 
256     key = gdbm_firstkey(dp->di_dbm);
257     while (key.dptr) {
258         item = PyString_FromStringAndSize(key.dptr, key.dsize);
259         if (item == NULL) {
260             free(key.dptr);
261             Py_DECREF(v);
262             return NULL;
263         }
264         err = PyList_Append(v, item);
265         Py_DECREF(item);
266         if (err != 0) {
267             free(key.dptr);
268             Py_DECREF(v);
269             return NULL;
270         }
271         nextkey = gdbm_nextkey(dp->di_dbm, key);
272         free(key.dptr);
273         key = nextkey;
274     }
275     return v;
276 }
277 
278 PyDoc_STRVAR(dbm_has_key__doc__,
279 "has_key(key) -> boolean\n\
280 Find out whether or not the database contains a given key.");
281 
282 static PyObject *
dbm_has_key(register dbmobject * dp,PyObject * args)283 dbm_has_key(register dbmobject *dp, PyObject *args)
284 {
285     datum key;
286 
287     if (!PyArg_ParseTuple(args, "s#:has_key", &key.dptr, &key.dsize))
288         return NULL;
289     check_dbmobject_open(dp);
290     return PyInt_FromLong((long) gdbm_exists(dp->di_dbm, key));
291 }
292 
293 PyDoc_STRVAR(dbm_firstkey__doc__,
294 "firstkey() -> key\n\
295 It's possible to loop over every key in the database using this method\n\
296 and the nextkey() method. The traversal is ordered by GDBM's internal\n\
297 hash values, and won't be sorted by the key values. This method\n\
298 returns the starting key.");
299 
300 static PyObject *
dbm_firstkey(register dbmobject * dp,PyObject * unused)301 dbm_firstkey(register dbmobject *dp, PyObject *unused)
302 {
303     register PyObject *v;
304     datum key;
305 
306     check_dbmobject_open(dp);
307     key = gdbm_firstkey(dp->di_dbm);
308     if (key.dptr) {
309         v = PyString_FromStringAndSize(key.dptr, key.dsize);
310         free(key.dptr);
311         return v;
312     }
313     else {
314         Py_INCREF(Py_None);
315         return Py_None;
316     }
317 }
318 
319 PyDoc_STRVAR(dbm_nextkey__doc__,
320 "nextkey(key) -> next_key\n\
321 Returns the key that follows key in the traversal.\n\
322 The following code prints every key in the database db, without having\n\
323 to create a list in memory that contains them all:\n\
324 \n\
325       k = db.firstkey()\n\
326       while k != None:\n\
327           print k\n\
328           k = db.nextkey(k)");
329 
330 static PyObject *
dbm_nextkey(register dbmobject * dp,PyObject * args)331 dbm_nextkey(register dbmobject *dp, PyObject *args)
332 {
333     register PyObject *v;
334     datum key, nextkey;
335 
336     if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize))
337         return NULL;
338     check_dbmobject_open(dp);
339     nextkey = gdbm_nextkey(dp->di_dbm, key);
340     if (nextkey.dptr) {
341         v = PyString_FromStringAndSize(nextkey.dptr, nextkey.dsize);
342         free(nextkey.dptr);
343         return v;
344     }
345     else {
346         Py_INCREF(Py_None);
347         return Py_None;
348     }
349 }
350 
351 PyDoc_STRVAR(dbm_reorganize__doc__,
352 "reorganize() -> None\n\
353 If you have carried out a lot of deletions and would like to shrink\n\
354 the space used by the GDBM file, this routine will reorganize the\n\
355 database. GDBM will not shorten the length of a database file except\n\
356 by using this reorganization; otherwise, deleted file space will be\n\
357 kept and reused as new (key,value) pairs are added.");
358 
359 static PyObject *
dbm_reorganize(register dbmobject * dp,PyObject * unused)360 dbm_reorganize(register dbmobject *dp, PyObject *unused)
361 {
362     check_dbmobject_open(dp);
363     errno = 0;
364     if (gdbm_reorganize(dp->di_dbm) < 0) {
365         if (errno != 0)
366             PyErr_SetFromErrno(DbmError);
367         else
368             PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
369         return NULL;
370     }
371     Py_INCREF(Py_None);
372     return Py_None;
373 }
374 
375 PyDoc_STRVAR(dbm_sync__doc__,
376 "sync() -> None\n\
377 When the database has been opened in fast mode, this method forces\n\
378 any unwritten data to be written to the disk.");
379 
380 static PyObject *
dbm_sync(register dbmobject * dp,PyObject * unused)381 dbm_sync(register dbmobject *dp, PyObject *unused)
382 {
383     check_dbmobject_open(dp);
384     gdbm_sync(dp->di_dbm);
385     Py_INCREF(Py_None);
386     return Py_None;
387 }
388 
389 static PyMethodDef dbm_methods[] = {
390     {"close",	  (PyCFunction)dbm_close,   METH_NOARGS, dbm_close__doc__},
391     {"keys",	  (PyCFunction)dbm_keys,    METH_NOARGS, dbm_keys__doc__},
392     {"has_key",   (PyCFunction)dbm_has_key, METH_VARARGS, dbm_has_key__doc__},
393     {"firstkey",  (PyCFunction)dbm_firstkey,METH_NOARGS, dbm_firstkey__doc__},
394     {"nextkey",	  (PyCFunction)dbm_nextkey, METH_VARARGS, dbm_nextkey__doc__},
395     {"reorganize",(PyCFunction)dbm_reorganize,METH_NOARGS, dbm_reorganize__doc__},
396     {"sync",      (PyCFunction)dbm_sync,    METH_NOARGS, dbm_sync__doc__},
397     {NULL,		NULL}		/* sentinel */
398 };
399 
400 static PyObject *
dbm_getattr(dbmobject * dp,char * name)401 dbm_getattr(dbmobject *dp, char *name)
402 {
403     return Py_FindMethod(dbm_methods, (PyObject *)dp, name);
404 }
405 
406 static PyTypeObject Dbmtype = {
407     PyVarObject_HEAD_INIT(0, 0)
408     "gdbm.gdbm",
409     sizeof(dbmobject),
410     0,
411     (destructor)dbm_dealloc,            /*tp_dealloc*/
412     0,                                  /*tp_print*/
413     (getattrfunc)dbm_getattr,           /*tp_getattr*/
414     0,                                  /*tp_setattr*/
415     0,                                  /*tp_compare*/
416     0,                                  /*tp_repr*/
417     0,                                  /*tp_as_number*/
418     &dbm_as_sequence,                   /*tp_as_sequence*/
419     &dbm_as_mapping,                    /*tp_as_mapping*/
420     0,                                  /*tp_hash*/
421     0,                                  /*tp_call*/
422     0,                                  /*tp_str*/
423     0,                                  /*tp_getattro*/
424     0,                                  /*tp_setattro*/
425     0,                                  /*tp_as_buffer*/
426     Py_TPFLAGS_DEFAULT,                 /*tp_xxx4*/
427     gdbm_object__doc__,                 /*tp_doc*/
428 };
429 
430 /* ----------------------------------------------------------------- */
431 
432 PyDoc_STRVAR(dbmopen__doc__,
433 "open(filename, [flags, [mode]])  -> dbm_object\n\
434 Open a dbm database and return a dbm object. The filename argument is\n\
435 the name of the database file.\n\
436 \n\
437 The optional flags argument can be 'r' (to open an existing database\n\
438 for reading only -- default), 'w' (to open an existing database for\n\
439 reading and writing), 'c' (which creates the database if it doesn't\n\
440 exist), or 'n' (which always creates a new empty database).\n\
441 \n\
442 Some versions of gdbm support additional flags which must be\n\
443 appended to one of the flags described above. The module constant\n\
444 'open_flags' is a string of valid additional flags. The 'f' flag\n\
445 opens the database in fast mode; altered data will not automatically\n\
446 be written to the disk after every change. This results in faster\n\
447 writes to the database, but may result in an inconsistent database\n\
448 if the program crashes while the database is still open. Use the\n\
449 sync() method to force any unwritten data to be written to the disk.\n\
450 The 's' flag causes all database operations to be synchronized to\n\
451 disk. The 'u' flag disables locking of the database file.\n\
452 \n\
453 The optional mode argument is the Unix mode of the file, used only\n\
454 when the database has to be created. It defaults to octal 0666. ");
455 
456 static PyObject *
dbmopen(PyObject * self,PyObject * args)457 dbmopen(PyObject *self, PyObject *args)
458 {
459     char *name;
460     char *flags = "r";
461     int iflags;
462     int mode = 0666;
463 
464     if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode))
465         return NULL;
466     switch (flags[0]) {
467     case 'r':
468         iflags = GDBM_READER;
469         break;
470     case 'w':
471         iflags = GDBM_WRITER;
472         break;
473     case 'c':
474         iflags = GDBM_WRCREAT;
475         break;
476     case 'n':
477         iflags = GDBM_NEWDB;
478         break;
479     default:
480         PyErr_SetString(DbmError,
481                         "First flag must be one of 'r', 'w', 'c' or 'n'");
482         return NULL;
483     }
484     for (flags++; *flags != '\0'; flags++) {
485         char buf[40];
486         switch (*flags) {
487 #ifdef GDBM_FAST
488             case 'f':
489                 iflags |= GDBM_FAST;
490                 break;
491 #endif
492 #ifdef GDBM_SYNC
493             case 's':
494                 iflags |= GDBM_SYNC;
495                 break;
496 #endif
497 #ifdef GDBM_NOLOCK
498             case 'u':
499                 iflags |= GDBM_NOLOCK;
500                 break;
501 #endif
502             default:
503                 PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
504                 	      *flags);
505                 PyErr_SetString(DbmError, buf);
506                 return NULL;
507         }
508     }
509 
510     return newdbmobject(name, iflags, mode);
511 }
512 
513 static char dbmmodule_open_flags[] = "rwcn"
514 #ifdef GDBM_FAST
515                                      "f"
516 #endif
517 #ifdef GDBM_SYNC
518                                      "s"
519 #endif
520 #ifdef GDBM_NOLOCK
521                                      "u"
522 #endif
523                                      ;
524 
525 static PyMethodDef dbmmodule_methods[] = {
526     { "open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__},
527     { 0, 0 },
528 };
529 
530 PyMODINIT_FUNC
initgdbm(void)531 initgdbm(void) {
532     PyObject *m, *d, *s;
533 
534     Dbmtype.ob_type = &PyType_Type;
535     m = Py_InitModule4("gdbm", dbmmodule_methods,
536                        gdbmmodule__doc__, (PyObject *)NULL,
537                        PYTHON_API_VERSION);
538     if (m == NULL)
539 	return;
540     d = PyModule_GetDict(m);
541     DbmError = PyErr_NewException("gdbm.error", NULL, NULL);
542     if (DbmError != NULL) {
543         PyDict_SetItemString(d, "error", DbmError);
544         s = PyString_FromString(dbmmodule_open_flags);
545         PyDict_SetItemString(d, "open_flags", s);
546         Py_DECREF(s);
547     }
548 }
549