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