• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* DBM module using dictionary interface */
3 
4 
5 #include "Python.h"
6 
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 
11 /* Some Linux systems install gdbm/ndbm.h, but not ndbm.h.  This supports
12  * whichever configure was able to locate.
13  */
14 #if defined(HAVE_NDBM_H)
15 #include <ndbm.h>
16 #if defined(PYOS_OS2) && !defined(PYCC_GCC)
17 static char *which_dbm = "ndbm";
18 #else
19 static char *which_dbm = "GNU gdbm";  /* EMX port of GDBM */
20 #endif
21 #elif defined(HAVE_GDBM_NDBM_H)
22 #include <gdbm/ndbm.h>
23 static char *which_dbm = "GNU gdbm";
24 #elif defined(HAVE_GDBM_DASH_NDBM_H)
25 #include <gdbm-ndbm.h>
26 static char *which_dbm = "GNU gdbm";
27 #elif defined(HAVE_BERKDB_H)
28 #include <db.h>
29 static char *which_dbm = "Berkeley DB";
30 #else
31 #error "No ndbm.h available!"
32 #endif
33 
34 typedef struct {
35     PyObject_HEAD
36     int di_size;        /* -1 means recompute */
37     DBM *di_dbm;
38 } dbmobject;
39 
40 static PyTypeObject Dbmtype;
41 
42 #define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype)
43 #define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
44                { PyErr_SetString(DbmError, "DBM object has already been closed"); \
45                  return NULL; }
46 
47 static PyObject *DbmError;
48 
49 static PyObject *
newdbmobject(char * file,int flags,int mode)50 newdbmobject(char *file, int flags, int mode)
51 {
52     dbmobject *dp;
53 
54     dp = PyObject_New(dbmobject, &Dbmtype);
55     if (dp == NULL)
56         return NULL;
57     dp->di_size = -1;
58     if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) {
59         PyErr_SetFromErrno(DbmError);
60         Py_DECREF(dp);
61         return NULL;
62     }
63     return (PyObject *)dp;
64 }
65 
66 /* Methods */
67 
68 static void
dbm_dealloc(register dbmobject * dp)69 dbm_dealloc(register dbmobject *dp)
70 {
71     if ( dp->di_dbm )
72         dbm_close(dp->di_dbm);
73     PyObject_Del(dp);
74 }
75 
76 static Py_ssize_t
dbm_length(dbmobject * dp)77 dbm_length(dbmobject *dp)
78 {
79     if (dp->di_dbm == NULL) {
80              PyErr_SetString(DbmError, "DBM object has already been closed");
81              return -1;
82     }
83     if ( dp->di_size < 0 ) {
84         datum key;
85         int size;
86 
87         size = 0;
88         for ( key=dbm_firstkey(dp->di_dbm); key.dptr;
89               key = dbm_nextkey(dp->di_dbm))
90             size++;
91         dp->di_size = size;
92     }
93     return dp->di_size;
94 }
95 
96 static PyObject *
dbm_subscript(dbmobject * dp,register PyObject * key)97 dbm_subscript(dbmobject *dp, register PyObject *key)
98 {
99     datum drec, krec;
100     int tmp_size;
101 
102     if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) )
103         return NULL;
104 
105     krec.dsize = tmp_size;
106     check_dbmobject_open(dp);
107     drec = dbm_fetch(dp->di_dbm, krec);
108     if ( drec.dptr == 0 ) {
109         PyErr_SetString(PyExc_KeyError,
110                         PyString_AS_STRING((PyStringObject *)key));
111         return NULL;
112     }
113     if ( dbm_error(dp->di_dbm) ) {
114         dbm_clearerr(dp->di_dbm);
115         PyErr_SetString(DbmError, "");
116         return NULL;
117     }
118     return PyString_FromStringAndSize(drec.dptr, drec.dsize);
119 }
120 
121 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)122 dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
123 {
124     datum krec, drec;
125     int tmp_size;
126 
127     if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
128         PyErr_SetString(PyExc_TypeError,
129                         "dbm mappings have string indices only");
130         return -1;
131     }
132     krec.dsize = tmp_size;
133     if (dp->di_dbm == NULL) {
134              PyErr_SetString(DbmError, "DBM object has already been closed");
135              return -1;
136     }
137     dp->di_size = -1;
138     if (w == NULL) {
139         if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
140             dbm_clearerr(dp->di_dbm);
141             PyErr_SetString(PyExc_KeyError,
142                           PyString_AS_STRING((PyStringObject *)v));
143             return -1;
144         }
145     } else {
146         if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
147             PyErr_SetString(PyExc_TypeError,
148                          "dbm mappings have string elements only");
149             return -1;
150         }
151         drec.dsize = tmp_size;
152         if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) {
153             dbm_clearerr(dp->di_dbm);
154             PyErr_SetString(DbmError,
155                             "cannot add item to database");
156             return -1;
157         }
158     }
159     if ( dbm_error(dp->di_dbm) ) {
160         dbm_clearerr(dp->di_dbm);
161         PyErr_SetString(DbmError, "");
162         return -1;
163     }
164     return 0;
165 }
166 
167 static int
dbm_contains(register dbmobject * dp,PyObject * v)168 dbm_contains(register dbmobject *dp, PyObject *v)
169 {
170     datum key, val;
171     char *ptr;
172     Py_ssize_t size;
173 
174     if (PyString_AsStringAndSize(v, &ptr, &size))
175         return -1;
176     key.dptr = ptr;
177     key.dsize = size;
178 
179     /* Expand check_dbmobject_open to return -1 */
180     if (dp->di_dbm == NULL) {
181         PyErr_SetString(DbmError, "DBM object has already been closed");
182         return -1;
183     }
184     val = dbm_fetch(dp->di_dbm, key);
185     return val.dptr != NULL;
186 }
187 
188 static PySequenceMethods dbm_as_sequence = {
189     (lenfunc)dbm_length,        /*_length*/
190     0,                          /*sq_concat*/
191     0,                          /*sq_repeat*/
192     0,                          /*sq_item*/
193     0,                          /*sq_slice*/
194     0,                          /*sq_ass_item*/
195     0,                          /*sq_ass_slice*/
196     (objobjproc)dbm_contains,   /*sq_contains*/
197     0,                          /*sq_inplace_concat*/
198     0                           /*sq_inplace_repeat*/
199 };
200 
201 static PyMappingMethods dbm_as_mapping = {
202     (lenfunc)dbm_length,                /*mp_length*/
203     (binaryfunc)dbm_subscript,          /*mp_subscript*/
204     (objobjargproc)dbm_ass_sub,         /*mp_ass_subscript*/
205 };
206 
207 static PyObject *
dbm__close(register dbmobject * dp,PyObject * unused)208 dbm__close(register dbmobject *dp, PyObject *unused)
209 {
210     if (dp->di_dbm)
211         dbm_close(dp->di_dbm);
212     dp->di_dbm = NULL;
213     Py_INCREF(Py_None);
214     return Py_None;
215 }
216 
217 static PyObject *
dbm_keys(register dbmobject * dp,PyObject * unused)218 dbm_keys(register dbmobject *dp, PyObject *unused)
219 {
220     register PyObject *v, *item;
221     datum key;
222     int err;
223 
224     check_dbmobject_open(dp);
225     v = PyList_New(0);
226     if (v == NULL)
227         return NULL;
228     for (key = dbm_firstkey(dp->di_dbm); key.dptr;
229          key = dbm_nextkey(dp->di_dbm)) {
230         item = PyString_FromStringAndSize(key.dptr, key.dsize);
231         if (item == NULL) {
232             Py_DECREF(v);
233             return NULL;
234         }
235         err = PyList_Append(v, item);
236         Py_DECREF(item);
237         if (err != 0) {
238             Py_DECREF(v);
239             return NULL;
240         }
241     }
242     return v;
243 }
244 
245 static PyObject *
dbm_has_key(register dbmobject * dp,PyObject * args)246 dbm_has_key(register dbmobject *dp, PyObject *args)
247 {
248     char *tmp_ptr;
249     datum key, val;
250     int tmp_size;
251 
252     if (!PyArg_ParseTuple(args, "s#:has_key", &tmp_ptr, &tmp_size))
253         return NULL;
254     key.dptr = tmp_ptr;
255     key.dsize = tmp_size;
256     check_dbmobject_open(dp);
257     val = dbm_fetch(dp->di_dbm, key);
258     return PyInt_FromLong(val.dptr != NULL);
259 }
260 
261 static PyObject *
dbm_get(register dbmobject * dp,PyObject * args)262 dbm_get(register dbmobject *dp, PyObject *args)
263 {
264     datum key, val;
265     PyObject *defvalue = Py_None;
266     char *tmp_ptr;
267     int tmp_size;
268 
269     if (!PyArg_ParseTuple(args, "s#|O:get",
270                           &tmp_ptr, &tmp_size, &defvalue))
271         return NULL;
272     key.dptr = tmp_ptr;
273     key.dsize = tmp_size;
274     check_dbmobject_open(dp);
275     val = dbm_fetch(dp->di_dbm, key);
276     if (val.dptr != NULL)
277         return PyString_FromStringAndSize(val.dptr, val.dsize);
278     else {
279         Py_INCREF(defvalue);
280         return defvalue;
281     }
282 }
283 
284 static PyObject *
dbm_setdefault(register dbmobject * dp,PyObject * args)285 dbm_setdefault(register dbmobject *dp, PyObject *args)
286 {
287     datum key, val;
288     PyObject *defvalue = NULL;
289     char *tmp_ptr;
290     int tmp_size;
291 
292     if (!PyArg_ParseTuple(args, "s#|S:setdefault",
293                           &tmp_ptr, &tmp_size, &defvalue))
294         return NULL;
295     key.dptr = tmp_ptr;
296     key.dsize = tmp_size;
297     check_dbmobject_open(dp);
298     val = dbm_fetch(dp->di_dbm, key);
299     if (val.dptr != NULL)
300         return PyString_FromStringAndSize(val.dptr, val.dsize);
301     if (defvalue == NULL) {
302         defvalue = PyString_FromStringAndSize(NULL, 0);
303         if (defvalue == NULL)
304             return NULL;
305     }
306     else
307         Py_INCREF(defvalue);
308     val.dptr = PyString_AS_STRING(defvalue);
309     val.dsize = PyString_GET_SIZE(defvalue);
310     if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) {
311         dbm_clearerr(dp->di_dbm);
312         PyErr_SetString(DbmError, "cannot add item to database");
313         return NULL;
314     }
315     return defvalue;
316 }
317 
318 static PyMethodDef dbm_methods[] = {
319     {"close",           (PyCFunction)dbm__close,        METH_NOARGS,
320      "close()\nClose the database."},
321     {"keys",            (PyCFunction)dbm_keys,          METH_NOARGS,
322      "keys() -> list\nReturn a list of all keys in the database."},
323     {"has_key",         (PyCFunction)dbm_has_key,       METH_VARARGS,
324      "has_key(key} -> boolean\nReturn true iff key is in the database."},
325     {"get",             (PyCFunction)dbm_get,           METH_VARARGS,
326      "get(key[, default]) -> value\n"
327      "Return the value for key if present, otherwise default."},
328     {"setdefault",      (PyCFunction)dbm_setdefault,    METH_VARARGS,
329      "setdefault(key[, default]) -> value\n"
330      "Return the value for key if present, otherwise default.  If key\n"
331      "is not in the database, it is inserted with default as the value."},
332     {NULL,              NULL}           /* sentinel */
333 };
334 
335 static PyObject *
dbm_getattr(dbmobject * dp,char * name)336 dbm_getattr(dbmobject *dp, char *name)
337 {
338     return Py_FindMethod(dbm_methods, (PyObject *)dp, name);
339 }
340 
341 static PyTypeObject Dbmtype = {
342     PyVarObject_HEAD_INIT(NULL, 0)
343     "dbm.dbm",
344     sizeof(dbmobject),
345     0,
346     (destructor)dbm_dealloc,  /*tp_dealloc*/
347     0,                            /*tp_print*/
348     (getattrfunc)dbm_getattr, /*tp_getattr*/
349     0,                            /*tp_setattr*/
350     0,                            /*tp_compare*/
351     0,                            /*tp_repr*/
352     0,                            /*tp_as_number*/
353     &dbm_as_sequence,     /*tp_as_sequence*/
354     &dbm_as_mapping,              /*tp_as_mapping*/
355     0,                    /*tp_hash*/
356     0,                    /*tp_call*/
357     0,                    /*tp_str*/
358     0,                    /*tp_getattro*/
359     0,                    /*tp_setattro*/
360     0,                    /*tp_as_buffer*/
361     Py_TPFLAGS_DEFAULT,   /*tp_xxx4*/
362 };
363 
364 /* ----------------------------------------------------------------- */
365 
366 static PyObject *
dbmopen(PyObject * self,PyObject * args)367 dbmopen(PyObject *self, PyObject *args)
368 {
369     char *name;
370     char *flags = "r";
371     int iflags;
372     int mode = 0666;
373 
374     if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) )
375         return NULL;
376     if ( strcmp(flags, "r") == 0 )
377         iflags = O_RDONLY;
378     else if ( strcmp(flags, "w") == 0 )
379         iflags = O_RDWR;
380     else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */
381         iflags = O_RDWR|O_CREAT;
382     else if ( strcmp(flags, "c") == 0 )
383         iflags = O_RDWR|O_CREAT;
384     else if ( strcmp(flags, "n") == 0 )
385         iflags = O_RDWR|O_CREAT|O_TRUNC;
386     else {
387         PyErr_SetString(DbmError,
388                         "arg 2 to open should be 'r', 'w', 'c', or 'n'");
389         return NULL;
390     }
391     return newdbmobject(name, iflags, mode);
392 }
393 
394 static PyMethodDef dbmmodule_methods[] = {
395     { "open", (PyCFunction)dbmopen, METH_VARARGS,
396       "open(path[, flag[, mode]]) -> mapping\n"
397       "Return a database object."},
398     { 0, 0 },
399 };
400 
401 PyMODINIT_FUNC
initdbm(void)402 initdbm(void) {
403     PyObject *m, *d, *s;
404 
405     Dbmtype.ob_type = &PyType_Type;
406     m = Py_InitModule("dbm", dbmmodule_methods);
407     if (m == NULL)
408         return;
409     d = PyModule_GetDict(m);
410     if (DbmError == NULL)
411         DbmError = PyErr_NewException("dbm.error", NULL, NULL);
412     s = PyString_FromString(which_dbm);
413     if (s != NULL) {
414         PyDict_SetItemString(d, "library", s);
415         Py_DECREF(s);
416     }
417     if (DbmError != NULL)
418         PyDict_SetItemString(d, "error", DbmError);
419 }
420