• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* DBM module using dictionary interface */
3 
4 
5 #define PY_SSIZE_T_CLEAN
6 #include "Python.h"
7 
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 
12 /* Some Linux systems install gdbm/ndbm.h, but not ndbm.h.  This supports
13  * whichever configure was able to locate.
14  */
15 #if defined(HAVE_NDBM_H)
16 #include <ndbm.h>
17 static const char which_dbm[] = "GNU gdbm";  /* EMX port of GDBM */
18 #elif defined(HAVE_GDBM_NDBM_H)
19 #include <gdbm/ndbm.h>
20 static const char which_dbm[] = "GNU gdbm";
21 #elif defined(HAVE_GDBM_DASH_NDBM_H)
22 #include <gdbm-ndbm.h>
23 static const char which_dbm[] = "GNU gdbm";
24 #elif defined(HAVE_BERKDB_H)
25 #include <db.h>
26 static const char which_dbm[] = "Berkeley DB";
27 #else
28 #error "No ndbm.h available!"
29 #endif
30 
31 /*[clinic input]
32 module _dbm
33 class _dbm.dbm "dbmobject *" "&Dbmtype"
34 [clinic start generated code]*/
35 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b1aa8756d16150e]*/
36 
37 typedef struct {
38     PyObject_HEAD
39     int di_size;        /* -1 means recompute */
40     DBM *di_dbm;
41 } dbmobject;
42 
43 #include "clinic/_dbmmodule.c.h"
44 
45 static PyTypeObject Dbmtype;
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, "DBM object has already been closed"); \
50                  return NULL; }
51 
52 static PyObject *DbmError;
53 
54 static PyObject *
newdbmobject(const char * file,int flags,int mode)55 newdbmobject(const char *file, int flags, int mode)
56 {
57     dbmobject *dp;
58 
59     dp = PyObject_New(dbmobject, &Dbmtype);
60     if (dp == NULL)
61         return NULL;
62     dp->di_size = -1;
63     /* See issue #19296 */
64     if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) {
65         PyErr_SetFromErrno(DbmError);
66         Py_DECREF(dp);
67         return NULL;
68     }
69     return (PyObject *)dp;
70 }
71 
72 /* Methods */
73 
74 static void
dbm_dealloc(dbmobject * dp)75 dbm_dealloc(dbmobject *dp)
76 {
77     if ( dp->di_dbm )
78         dbm_close(dp->di_dbm);
79     PyObject_Del(dp);
80 }
81 
82 static Py_ssize_t
dbm_length(dbmobject * dp)83 dbm_length(dbmobject *dp)
84 {
85     if (dp->di_dbm == NULL) {
86              PyErr_SetString(DbmError, "DBM object has already been closed");
87              return -1;
88     }
89     if ( dp->di_size < 0 ) {
90         datum key;
91         int size;
92 
93         size = 0;
94         for ( key=dbm_firstkey(dp->di_dbm); key.dptr;
95               key = dbm_nextkey(dp->di_dbm))
96             size++;
97         dp->di_size = size;
98     }
99     return dp->di_size;
100 }
101 
102 static PyObject *
dbm_subscript(dbmobject * dp,PyObject * key)103 dbm_subscript(dbmobject *dp, PyObject *key)
104 {
105     datum drec, krec;
106     Py_ssize_t tmp_size;
107 
108     if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) )
109         return NULL;
110 
111     krec.dsize = tmp_size;
112     check_dbmobject_open(dp);
113     drec = dbm_fetch(dp->di_dbm, krec);
114     if ( drec.dptr == 0 ) {
115         PyErr_SetObject(PyExc_KeyError, key);
116         return NULL;
117     }
118     if ( dbm_error(dp->di_dbm) ) {
119         dbm_clearerr(dp->di_dbm);
120         PyErr_SetString(DbmError, "");
121         return NULL;
122     }
123     return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
124 }
125 
126 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)127 dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
128 {
129     datum krec, drec;
130     Py_ssize_t tmp_size;
131 
132     if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
133         PyErr_SetString(PyExc_TypeError,
134                         "dbm mappings have bytes or string keys only");
135         return -1;
136     }
137     krec.dsize = tmp_size;
138     if (dp->di_dbm == NULL) {
139              PyErr_SetString(DbmError, "DBM object has already been closed");
140              return -1;
141     }
142     dp->di_size = -1;
143     if (w == NULL) {
144         if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
145             dbm_clearerr(dp->di_dbm);
146             PyErr_SetObject(PyExc_KeyError, v);
147             return -1;
148         }
149     } else {
150         if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
151             PyErr_SetString(PyExc_TypeError,
152                  "dbm mappings have byte or string elements only");
153             return -1;
154         }
155         drec.dsize = tmp_size;
156         if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) {
157             dbm_clearerr(dp->di_dbm);
158             PyErr_SetString(DbmError,
159                             "cannot add item to database");
160             return -1;
161         }
162     }
163     if ( dbm_error(dp->di_dbm) ) {
164         dbm_clearerr(dp->di_dbm);
165         PyErr_SetString(DbmError, "");
166         return -1;
167     }
168     return 0;
169 }
170 
171 static PyMappingMethods dbm_as_mapping = {
172     (lenfunc)dbm_length,                /*mp_length*/
173     (binaryfunc)dbm_subscript,          /*mp_subscript*/
174     (objobjargproc)dbm_ass_sub,         /*mp_ass_subscript*/
175 };
176 
177 /*[clinic input]
178 _dbm.dbm.close
179 
180 Close the database.
181 [clinic start generated code]*/
182 
183 static PyObject *
_dbm_dbm_close_impl(dbmobject * self)184 _dbm_dbm_close_impl(dbmobject *self)
185 /*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/
186 {
187     if (self->di_dbm)
188         dbm_close(self->di_dbm);
189     self->di_dbm = NULL;
190     Py_INCREF(Py_None);
191     return Py_None;
192 }
193 
194 /*[clinic input]
195 _dbm.dbm.keys
196 
197 Return a list of all keys in the database.
198 [clinic start generated code]*/
199 
200 static PyObject *
_dbm_dbm_keys_impl(dbmobject * self)201 _dbm_dbm_keys_impl(dbmobject *self)
202 /*[clinic end generated code: output=434549f7c121b33c input=d210ba778cd9c68a]*/
203 {
204     PyObject *v, *item;
205     datum key;
206     int err;
207 
208     check_dbmobject_open(self);
209     v = PyList_New(0);
210     if (v == NULL)
211         return NULL;
212     for (key = dbm_firstkey(self->di_dbm); key.dptr;
213          key = dbm_nextkey(self->di_dbm)) {
214         item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
215         if (item == NULL) {
216             Py_DECREF(v);
217             return NULL;
218         }
219         err = PyList_Append(v, item);
220         Py_DECREF(item);
221         if (err != 0) {
222             Py_DECREF(v);
223             return NULL;
224         }
225     }
226     return v;
227 }
228 
229 static int
dbm_contains(PyObject * self,PyObject * arg)230 dbm_contains(PyObject *self, PyObject *arg)
231 {
232     dbmobject *dp = (dbmobject *)self;
233     datum key, val;
234     Py_ssize_t size;
235 
236     if ((dp)->di_dbm == NULL) {
237         PyErr_SetString(DbmError,
238                         "DBM object has already been closed");
239          return -1;
240     }
241     if (PyUnicode_Check(arg)) {
242         key.dptr = PyUnicode_AsUTF8AndSize(arg, &size);
243         key.dsize = size;
244         if (key.dptr == NULL)
245             return -1;
246     }
247     else if (!PyBytes_Check(arg)) {
248         PyErr_Format(PyExc_TypeError,
249                      "dbm key must be bytes or string, not %.100s",
250                      arg->ob_type->tp_name);
251         return -1;
252     }
253     else {
254         key.dptr = PyBytes_AS_STRING(arg);
255         key.dsize = PyBytes_GET_SIZE(arg);
256     }
257     val = dbm_fetch(dp->di_dbm, key);
258     return val.dptr != NULL;
259 }
260 
261 static PySequenceMethods dbm_as_sequence = {
262     0,                          /* sq_length */
263     0,                          /* sq_concat */
264     0,                          /* sq_repeat */
265     0,                          /* sq_item */
266     0,                          /* sq_slice */
267     0,                          /* sq_ass_item */
268     0,                          /* sq_ass_slice */
269     dbm_contains,               /* sq_contains */
270     0,                          /* sq_inplace_concat */
271     0,                          /* sq_inplace_repeat */
272 };
273 
274 /*[clinic input]
275 _dbm.dbm.get
276 
277     key: str(accept={str, robuffer}, zeroes=True)
278     default: object(c_default="NULL") = b''
279     /
280 
281 Return the value for key if present, otherwise default.
282 [clinic start generated code]*/
283 
284 static PyObject *
_dbm_dbm_get_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length,PyObject * default_value)285 _dbm_dbm_get_impl(dbmobject *self, const char *key,
286                   Py_ssize_clean_t key_length, PyObject *default_value)
287 /*[clinic end generated code: output=b44f95eba8203d93 input=a3a279957f85eb6d]*/
288 /*[clinic end generated code: output=4f5c0e523eaf1251 input=9402c0af8582dc69]*/
289 {
290     datum dbm_key, val;
291 
292     dbm_key.dptr = (char *)key;
293     dbm_key.dsize = key_length;
294     check_dbmobject_open(self);
295     val = dbm_fetch(self->di_dbm, dbm_key);
296     if (val.dptr != NULL)
297         return PyBytes_FromStringAndSize(val.dptr, val.dsize);
298 
299     Py_INCREF(default_value);
300     return default_value;
301 }
302 
303 /*[clinic input]
304 _dbm.dbm.setdefault
305     key: str(accept={str, robuffer}, zeroes=True)
306     default: object(c_default="NULL") = b''
307     /
308 
309 Return the value for key if present, otherwise default.
310 
311 If key is not in the database, it is inserted with default as the value.
312 [clinic start generated code]*/
313 
314 static PyObject *
_dbm_dbm_setdefault_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length,PyObject * default_value)315 _dbm_dbm_setdefault_impl(dbmobject *self, const char *key,
316                          Py_ssize_clean_t key_length,
317                          PyObject *default_value)
318 /*[clinic end generated code: output=52545886cf272161 input=bf40c48edaca01d6]*/
319 {
320     datum dbm_key, val;
321     Py_ssize_t tmp_size;
322 
323     dbm_key.dptr = (char *)key;
324     dbm_key.dsize = key_length;
325     check_dbmobject_open(self);
326     val = dbm_fetch(self->di_dbm, dbm_key);
327     if (val.dptr != NULL)
328         return PyBytes_FromStringAndSize(val.dptr, val.dsize);
329     if (default_value == NULL) {
330         default_value = PyBytes_FromStringAndSize(NULL, 0);
331         if (default_value == NULL)
332             return NULL;
333         val.dptr = NULL;
334         val.dsize = 0;
335     }
336     else {
337         if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
338             PyErr_SetString(PyExc_TypeError,
339                 "dbm mappings have byte string elements only");
340             return NULL;
341         }
342         val.dsize = tmp_size;
343         Py_INCREF(default_value);
344     }
345     if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) {
346         dbm_clearerr(self->di_dbm);
347         PyErr_SetString(DbmError, "cannot add item to database");
348         Py_DECREF(default_value);
349         return NULL;
350     }
351     return default_value;
352 }
353 
354 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)355 dbm__enter__(PyObject *self, PyObject *args)
356 {
357     Py_INCREF(self);
358     return self;
359 }
360 
361 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)362 dbm__exit__(PyObject *self, PyObject *args)
363 {
364     _Py_IDENTIFIER(close);
365     return _PyObject_CallMethodId(self, &PyId_close, NULL);
366 }
367 
368 
369 static PyMethodDef dbm_methods[] = {
370     _DBM_DBM_CLOSE_METHODDEF
371     _DBM_DBM_KEYS_METHODDEF
372     _DBM_DBM_GET_METHODDEF
373     _DBM_DBM_SETDEFAULT_METHODDEF
374     {"__enter__", dbm__enter__, METH_NOARGS, NULL},
375     {"__exit__",  dbm__exit__, METH_VARARGS, NULL},
376     {NULL,              NULL}           /* sentinel */
377 };
378 
379 static PyTypeObject Dbmtype = {
380     PyVarObject_HEAD_INIT(NULL, 0)
381     "_dbm.dbm",
382     sizeof(dbmobject),
383     0,
384     (destructor)dbm_dealloc,  /*tp_dealloc*/
385     0,                            /*tp_print*/
386     0,                        /*tp_getattr*/
387     0,                            /*tp_setattr*/
388     0,                            /*tp_reserved*/
389     0,                            /*tp_repr*/
390     0,                            /*tp_as_number*/
391     &dbm_as_sequence,             /*tp_as_sequence*/
392     &dbm_as_mapping,              /*tp_as_mapping*/
393     0,                    /*tp_hash*/
394     0,                    /*tp_call*/
395     0,                    /*tp_str*/
396     0,                    /*tp_getattro*/
397     0,                    /*tp_setattro*/
398     0,                    /*tp_as_buffer*/
399     Py_TPFLAGS_DEFAULT,   /*tp_flags*/
400     0,                        /*tp_doc*/
401     0,                        /*tp_traverse*/
402     0,                        /*tp_clear*/
403     0,                        /*tp_richcompare*/
404     0,                        /*tp_weaklistoffset*/
405     0,                        /*tp_iter*/
406     0,                        /*tp_iternext*/
407     dbm_methods,          /*tp_methods*/
408 };
409 
410 /* ----------------------------------------------------------------- */
411 
412 /*[clinic input]
413 
414 _dbm.open as dbmopen
415 
416     filename: str
417         The filename to open.
418 
419     flags: str="r"
420         How to open the file.  "r" for reading, "w" for writing, etc.
421 
422     mode: int(py_default="0o666") = 0o666
423         If creating a new file, the mode bits for the new file
424         (e.g. os.O_RDWR).
425 
426     /
427 
428 Return a database object.
429 
430 [clinic start generated code]*/
431 
432 static PyObject *
dbmopen_impl(PyObject * module,const char * filename,const char * flags,int mode)433 dbmopen_impl(PyObject *module, const char *filename, const char *flags,
434              int mode)
435 /*[clinic end generated code: output=5fade8cf16e0755f input=226334bade5764e6]*/
436 {
437     int iflags;
438 
439     if ( strcmp(flags, "r") == 0 )
440         iflags = O_RDONLY;
441     else if ( strcmp(flags, "w") == 0 )
442         iflags = O_RDWR;
443     else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */
444         iflags = O_RDWR|O_CREAT;
445     else if ( strcmp(flags, "c") == 0 )
446         iflags = O_RDWR|O_CREAT;
447     else if ( strcmp(flags, "n") == 0 )
448         iflags = O_RDWR|O_CREAT|O_TRUNC;
449     else {
450         PyErr_SetString(DbmError,
451                         "arg 2 to open should be 'r', 'w', 'c', or 'n'");
452         return NULL;
453     }
454     return newdbmobject(filename, iflags, mode);
455 }
456 
457 static PyMethodDef dbmmodule_methods[] = {
458     DBMOPEN_METHODDEF
459     { 0, 0 },
460 };
461 
462 
463 static struct PyModuleDef _dbmmodule = {
464     PyModuleDef_HEAD_INIT,
465     "_dbm",
466     NULL,
467     -1,
468     dbmmodule_methods,
469     NULL,
470     NULL,
471     NULL,
472     NULL
473 };
474 
475 PyMODINIT_FUNC
PyInit__dbm(void)476 PyInit__dbm(void) {
477     PyObject *m, *d, *s;
478 
479     if (PyType_Ready(&Dbmtype) < 0)
480         return NULL;
481     m = PyModule_Create(&_dbmmodule);
482     if (m == NULL)
483         return NULL;
484     d = PyModule_GetDict(m);
485     if (DbmError == NULL)
486         DbmError = PyErr_NewException("_dbm.error",
487                                       PyExc_IOError, NULL);
488     s = PyUnicode_FromString(which_dbm);
489     if (s != NULL) {
490         PyDict_SetItemString(d, "library", s);
491         Py_DECREF(s);
492     }
493     if (DbmError != NULL)
494         PyDict_SetItemString(d, "error", DbmError);
495     if (PyErr_Occurred()) {
496         Py_DECREF(m);
497         m = NULL;
498     }
499     return m;
500 }
501