1
2 /* DBM module using dictionary interface */
3 /* Author: Anthony Baxter, after dbmmodule.c */
4 /* Doc strings: Mitch Chapman */
5
6 #define PY_SSIZE_T_CLEAN
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_SetFromErrnoWithFilename(DbmError, file);
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 // Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size).
123 // This function is needed to support PY_SSIZE_T_CLEAN.
124 // Return 1 on success, same to PyArg_Parse().
125 static int
parse_datum(PyObject * o,datum * d,const char * failmsg)126 parse_datum(PyObject *o, datum *d, const char *failmsg)
127 {
128 Py_ssize_t size;
129 if (!PyArg_Parse(o, "s#", &d->dptr, &size)) {
130 if (failmsg != NULL) {
131 PyErr_SetString(PyExc_TypeError, failmsg);
132 }
133 return 0;
134 }
135 if (INT_MAX < size) {
136 PyErr_SetString(PyExc_OverflowError, "size does not fit in an int");
137 return 0;
138 }
139 d->dsize = size;
140 return 1;
141 }
142
143 static PyObject *
dbm_subscript(dbmobject * dp,PyObject * key)144 dbm_subscript(dbmobject *dp, PyObject *key)
145 {
146 PyObject *v;
147 datum drec, krec;
148
149 if (!parse_datum(key, &krec, NULL)) {
150 return NULL;
151 }
152 if (dp->di_dbm == NULL) {
153 PyErr_SetString(DbmError,
154 "GDBM object has already been closed");
155 return NULL;
156 }
157 drec = gdbm_fetch(dp->di_dbm, krec);
158 if (drec.dptr == 0) {
159 PyErr_SetObject(PyExc_KeyError, key);
160 return NULL;
161 }
162 v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
163 free(drec.dptr);
164 return v;
165 }
166
167 /*[clinic input]
168 _gdbm.gdbm.get
169
170 key: object
171 default: object = None
172 /
173
174 Get the value for key, or default if not present.
175 [clinic start generated code]*/
176
177 static PyObject *
_gdbm_gdbm_get_impl(dbmobject * self,PyObject * key,PyObject * default_value)178 _gdbm_gdbm_get_impl(dbmobject *self, PyObject *key, PyObject *default_value)
179 /*[clinic end generated code: output=19b7c585ad4f554a input=a9c20423f34c17b6]*/
180 {
181 PyObject *res;
182
183 res = dbm_subscript(self, key);
184 if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
185 PyErr_Clear();
186 Py_INCREF(default_value);
187 return default_value;
188 }
189 return res;
190 }
191
192 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)193 dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
194 {
195 datum krec, drec;
196 const char *failmsg = "gdbm mappings have bytes or string indices only";
197
198 if (!parse_datum(v, &krec, failmsg)) {
199 return -1;
200 }
201 if (dp->di_dbm == NULL) {
202 PyErr_SetString(DbmError,
203 "GDBM object has already been closed");
204 return -1;
205 }
206 dp->di_size = -1;
207 if (w == NULL) {
208 if (gdbm_delete(dp->di_dbm, krec) < 0) {
209 if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
210 PyErr_SetObject(PyExc_KeyError, v);
211 }
212 else {
213 PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
214 }
215 return -1;
216 }
217 }
218 else {
219 if (!parse_datum(w, &drec, failmsg)) {
220 return -1;
221 }
222 errno = 0;
223 if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
224 if (errno != 0)
225 PyErr_SetFromErrno(DbmError);
226 else
227 PyErr_SetString(DbmError,
228 gdbm_strerror(gdbm_errno));
229 return -1;
230 }
231 }
232 return 0;
233 }
234
235 /*[clinic input]
236 _gdbm.gdbm.setdefault
237
238 key: object
239 default: object = None
240 /
241
242 Get value for key, or set it to default and return default if not present.
243 [clinic start generated code]*/
244
245 static PyObject *
_gdbm_gdbm_setdefault_impl(dbmobject * self,PyObject * key,PyObject * default_value)246 _gdbm_gdbm_setdefault_impl(dbmobject *self, PyObject *key,
247 PyObject *default_value)
248 /*[clinic end generated code: output=88760ee520329012 input=0db46b69e9680171]*/
249 {
250 PyObject *res;
251
252 res = dbm_subscript(self, key);
253 if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
254 PyErr_Clear();
255 if (dbm_ass_sub(self, key, default_value) < 0)
256 return NULL;
257 return dbm_subscript(self, key);
258 }
259 return res;
260 }
261
262 static PyMappingMethods dbm_as_mapping = {
263 (lenfunc)dbm_length, /*mp_length*/
264 (binaryfunc)dbm_subscript, /*mp_subscript*/
265 (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/
266 };
267
268 /*[clinic input]
269 _gdbm.gdbm.close
270
271 Close the database.
272 [clinic start generated code]*/
273
274 static PyObject *
_gdbm_gdbm_close_impl(dbmobject * self)275 _gdbm_gdbm_close_impl(dbmobject *self)
276 /*[clinic end generated code: output=23512a594598b563 input=0a203447379b45fd]*/
277 {
278 if (self->di_dbm)
279 gdbm_close(self->di_dbm);
280 self->di_dbm = NULL;
281 Py_RETURN_NONE;
282 }
283
284 /* XXX Should return a set or a set view */
285 /*[clinic input]
286 _gdbm.gdbm.keys
287
288 Get a list of all keys in the database.
289 [clinic start generated code]*/
290
291 static PyObject *
_gdbm_gdbm_keys_impl(dbmobject * self)292 _gdbm_gdbm_keys_impl(dbmobject *self)
293 /*[clinic end generated code: output=cb4b1776c3645dcc input=1832ee0a3132cfaf]*/
294 {
295 PyObject *v, *item;
296 datum key, nextkey;
297 int err;
298
299 if (self == NULL || !is_dbmobject(self)) {
300 PyErr_BadInternalCall();
301 return NULL;
302 }
303 check_dbmobject_open(self);
304
305 v = PyList_New(0);
306 if (v == NULL)
307 return NULL;
308
309 key = gdbm_firstkey(self->di_dbm);
310 while (key.dptr) {
311 item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
312 if (item == NULL) {
313 free(key.dptr);
314 Py_DECREF(v);
315 return NULL;
316 }
317 err = PyList_Append(v, item);
318 Py_DECREF(item);
319 if (err != 0) {
320 free(key.dptr);
321 Py_DECREF(v);
322 return NULL;
323 }
324 nextkey = gdbm_nextkey(self->di_dbm, key);
325 free(key.dptr);
326 key = nextkey;
327 }
328 return v;
329 }
330
331 static int
dbm_contains(PyObject * self,PyObject * arg)332 dbm_contains(PyObject *self, PyObject *arg)
333 {
334 dbmobject *dp = (dbmobject *)self;
335 datum key;
336 Py_ssize_t size;
337
338 if ((dp)->di_dbm == NULL) {
339 PyErr_SetString(DbmError,
340 "GDBM object has already been closed");
341 return -1;
342 }
343 if (PyUnicode_Check(arg)) {
344 key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
345 key.dsize = size;
346 if (key.dptr == NULL)
347 return -1;
348 }
349 else if (!PyBytes_Check(arg)) {
350 PyErr_Format(PyExc_TypeError,
351 "gdbm key must be bytes or string, not %.100s",
352 arg->ob_type->tp_name);
353 return -1;
354 }
355 else {
356 key.dptr = PyBytes_AS_STRING(arg);
357 key.dsize = PyBytes_GET_SIZE(arg);
358 }
359 return gdbm_exists(dp->di_dbm, key);
360 }
361
362 static PySequenceMethods dbm_as_sequence = {
363 0, /* sq_length */
364 0, /* sq_concat */
365 0, /* sq_repeat */
366 0, /* sq_item */
367 0, /* sq_slice */
368 0, /* sq_ass_item */
369 0, /* sq_ass_slice */
370 dbm_contains, /* sq_contains */
371 0, /* sq_inplace_concat */
372 0, /* sq_inplace_repeat */
373 };
374
375 /*[clinic input]
376 _gdbm.gdbm.firstkey
377
378 Return the starting key for the traversal.
379
380 It's possible to loop over every key in the database using this method
381 and the nextkey() method. The traversal is ordered by GDBM's internal
382 hash values, and won't be sorted by the key values.
383 [clinic start generated code]*/
384
385 static PyObject *
_gdbm_gdbm_firstkey_impl(dbmobject * self)386 _gdbm_gdbm_firstkey_impl(dbmobject *self)
387 /*[clinic end generated code: output=9ff85628d84b65d2 input=0dbd6a335d69bba0]*/
388 {
389 PyObject *v;
390 datum key;
391
392 check_dbmobject_open(self);
393 key = gdbm_firstkey(self->di_dbm);
394 if (key.dptr) {
395 v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
396 free(key.dptr);
397 return v;
398 }
399 else {
400 Py_RETURN_NONE;
401 }
402 }
403
404 /*[clinic input]
405 _gdbm.gdbm.nextkey
406
407 key: str(accept={str, robuffer}, zeroes=True)
408 /
409
410 Returns the key that follows key in the traversal.
411
412 The following code prints every key in the database db, without having
413 to create a list in memory that contains them all:
414
415 k = db.firstkey()
416 while k != None:
417 print(k)
418 k = db.nextkey(k)
419 [clinic start generated code]*/
420
421 static PyObject *
_gdbm_gdbm_nextkey_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length)422 _gdbm_gdbm_nextkey_impl(dbmobject *self, const char *key,
423 Py_ssize_clean_t key_length)
424 /*[clinic end generated code: output=192ab892de6eb2f6 input=1f1606943614e36f]*/
425 {
426 PyObject *v;
427 datum dbm_key, nextkey;
428
429 dbm_key.dptr = (char *)key;
430 dbm_key.dsize = key_length;
431 check_dbmobject_open(self);
432 nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
433 if (nextkey.dptr) {
434 v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
435 free(nextkey.dptr);
436 return v;
437 }
438 else {
439 Py_RETURN_NONE;
440 }
441 }
442
443 /*[clinic input]
444 _gdbm.gdbm.reorganize
445
446 Reorganize the database.
447
448 If you have carried out a lot of deletions and would like to shrink
449 the space used by the GDBM file, this routine will reorganize the
450 database. GDBM will not shorten the length of a database file except
451 by using this reorganization; otherwise, deleted file space will be
452 kept and reused as new (key,value) pairs are added.
453 [clinic start generated code]*/
454
455 static PyObject *
_gdbm_gdbm_reorganize_impl(dbmobject * self)456 _gdbm_gdbm_reorganize_impl(dbmobject *self)
457 /*[clinic end generated code: output=38d9624df92e961d input=f6bea85bcfd40dd2]*/
458 {
459 check_dbmobject_open(self);
460 errno = 0;
461 if (gdbm_reorganize(self->di_dbm) < 0) {
462 if (errno != 0)
463 PyErr_SetFromErrno(DbmError);
464 else
465 PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
466 return NULL;
467 }
468 Py_RETURN_NONE;
469 }
470
471 /*[clinic input]
472 _gdbm.gdbm.sync
473
474 Flush the database to the disk file.
475
476 When the database has been opened in fast mode, this method forces
477 any unwritten data to be written to the disk.
478 [clinic start generated code]*/
479
480 static PyObject *
_gdbm_gdbm_sync_impl(dbmobject * self)481 _gdbm_gdbm_sync_impl(dbmobject *self)
482 /*[clinic end generated code: output=488b15f47028f125 input=2a47d2c9e153ab8a]*/
483 {
484 check_dbmobject_open(self);
485 gdbm_sync(self->di_dbm);
486 Py_RETURN_NONE;
487 }
488
489 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)490 dbm__enter__(PyObject *self, PyObject *args)
491 {
492 Py_INCREF(self);
493 return self;
494 }
495
496 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)497 dbm__exit__(PyObject *self, PyObject *args)
498 {
499 _Py_IDENTIFIER(close);
500 return _PyObject_CallMethodId(self, &PyId_close, NULL);
501 }
502
503 static PyMethodDef dbm_methods[] = {
504 _GDBM_GDBM_CLOSE_METHODDEF
505 _GDBM_GDBM_KEYS_METHODDEF
506 _GDBM_GDBM_FIRSTKEY_METHODDEF
507 _GDBM_GDBM_NEXTKEY_METHODDEF
508 _GDBM_GDBM_REORGANIZE_METHODDEF
509 _GDBM_GDBM_SYNC_METHODDEF
510 _GDBM_GDBM_GET_METHODDEF
511 _GDBM_GDBM_SETDEFAULT_METHODDEF
512 {"__enter__", dbm__enter__, METH_NOARGS, NULL},
513 {"__exit__", dbm__exit__, METH_VARARGS, NULL},
514 {NULL, NULL} /* sentinel */
515 };
516
517 static PyTypeObject Dbmtype = {
518 PyVarObject_HEAD_INIT(0, 0)
519 "_gdbm.gdbm",
520 sizeof(dbmobject),
521 0,
522 (destructor)dbm_dealloc, /*tp_dealloc*/
523 0, /*tp_vectorcall_offset*/
524 0, /*tp_getattr*/
525 0, /*tp_setattr*/
526 0, /*tp_as_async*/
527 0, /*tp_repr*/
528 0, /*tp_as_number*/
529 &dbm_as_sequence, /*tp_as_sequence*/
530 &dbm_as_mapping, /*tp_as_mapping*/
531 0, /*tp_hash*/
532 0, /*tp_call*/
533 0, /*tp_str*/
534 0, /*tp_getattro*/
535 0, /*tp_setattro*/
536 0, /*tp_as_buffer*/
537 Py_TPFLAGS_DEFAULT, /*tp_xxx4*/
538 gdbm_object__doc__, /*tp_doc*/
539 0, /*tp_traverse*/
540 0, /*tp_clear*/
541 0, /*tp_richcompare*/
542 0, /*tp_weaklistoffset*/
543 0, /*tp_iter*/
544 0, /*tp_iternext*/
545 dbm_methods, /*tp_methods*/
546 };
547
548 /* ----------------------------------------------------------------- */
549
550 /*[clinic input]
551 _gdbm.open as dbmopen
552 filename: unicode
553 flags: str="r"
554 mode: int(py_default="0o666") = 0o666
555 /
556
557 Open a dbm database and return a dbm object.
558
559 The filename argument is the name of the database file.
560
561 The optional flags argument can be 'r' (to open an existing database
562 for reading only -- default), 'w' (to open an existing database for
563 reading and writing), 'c' (which creates the database if it doesn't
564 exist), or 'n' (which always creates a new empty database).
565
566 Some versions of gdbm support additional flags which must be
567 appended to one of the flags described above. The module constant
568 'open_flags' is a string of valid additional flags. The 'f' flag
569 opens the database in fast mode; altered data will not automatically
570 be written to the disk after every change. This results in faster
571 writes to the database, but may result in an inconsistent database
572 if the program crashes while the database is still open. Use the
573 sync() method to force any unwritten data to be written to the disk.
574 The 's' flag causes all database operations to be synchronized to
575 disk. The 'u' flag disables locking of the database file.
576
577 The optional mode argument is the Unix mode of the file, used only
578 when the database has to be created. It defaults to octal 0o666.
579 [clinic start generated code]*/
580
581 static PyObject *
dbmopen_impl(PyObject * module,PyObject * filename,const char * flags,int mode)582 dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
583 int mode)
584 /*[clinic end generated code: output=9527750f5df90764 input=3be0b0875974b928]*/
585 {
586 int iflags;
587
588 switch (flags[0]) {
589 case 'r':
590 iflags = GDBM_READER;
591 break;
592 case 'w':
593 iflags = GDBM_WRITER;
594 break;
595 case 'c':
596 iflags = GDBM_WRCREAT;
597 break;
598 case 'n':
599 iflags = GDBM_NEWDB;
600 break;
601 default:
602 PyErr_SetString(DbmError,
603 "First flag must be one of 'r', 'w', 'c' or 'n'");
604 return NULL;
605 }
606 for (flags++; *flags != '\0'; flags++) {
607 char buf[40];
608 switch (*flags) {
609 #ifdef GDBM_FAST
610 case 'f':
611 iflags |= GDBM_FAST;
612 break;
613 #endif
614 #ifdef GDBM_SYNC
615 case 's':
616 iflags |= GDBM_SYNC;
617 break;
618 #endif
619 #ifdef GDBM_NOLOCK
620 case 'u':
621 iflags |= GDBM_NOLOCK;
622 break;
623 #endif
624 default:
625 PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
626 *flags);
627 PyErr_SetString(DbmError, buf);
628 return NULL;
629 }
630 }
631
632 PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename);
633 if (filenamebytes == NULL) {
634 return NULL;
635 }
636 const char *name = PyBytes_AS_STRING(filenamebytes);
637 if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
638 Py_DECREF(filenamebytes);
639 PyErr_SetString(PyExc_ValueError, "embedded null character");
640 return NULL;
641 }
642 PyObject *self = newdbmobject(name, iflags, mode);
643 Py_DECREF(filenamebytes);
644 return self;
645 }
646
647 static const char dbmmodule_open_flags[] = "rwcn"
648 #ifdef GDBM_FAST
649 "f"
650 #endif
651 #ifdef GDBM_SYNC
652 "s"
653 #endif
654 #ifdef GDBM_NOLOCK
655 "u"
656 #endif
657 ;
658
659 static PyMethodDef dbmmodule_methods[] = {
660 DBMOPEN_METHODDEF
661 { 0, 0 },
662 };
663
664
665 static struct PyModuleDef _gdbmmodule = {
666 PyModuleDef_HEAD_INIT,
667 "_gdbm",
668 gdbmmodule__doc__,
669 -1,
670 dbmmodule_methods,
671 NULL,
672 NULL,
673 NULL,
674 NULL
675 };
676
677 PyMODINIT_FUNC
PyInit__gdbm(void)678 PyInit__gdbm(void) {
679 PyObject *m;
680
681 if (PyType_Ready(&Dbmtype) < 0)
682 return NULL;
683 m = PyModule_Create(&_gdbmmodule);
684 if (m == NULL) {
685 return NULL;
686 }
687
688 DbmError = PyErr_NewException("_gdbm.error", PyExc_OSError, NULL);
689 if (DbmError == NULL) {
690 goto error;
691 }
692 Py_INCREF(DbmError);
693 if (PyModule_AddObject(m, "error", DbmError) < 0) {
694 Py_DECREF(DbmError);
695 goto error;
696 }
697
698 if (PyModule_AddStringConstant(m, "open_flags",
699 dbmmodule_open_flags) < 0) {
700 goto error;
701 }
702
703 #if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
704 defined(GDBM_VERSION_PATCH)
705 PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
706 GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
707 if (obj == NULL) {
708 goto error;
709 }
710 if (PyModule_AddObject(m, "_GDBM_VERSION", obj) < 0) {
711 Py_DECREF(obj);
712 goto error;
713 }
714 #endif
715
716 return m;
717
718 error:
719 Py_DECREF(m);
720 return NULL;
721 }
722