1 
2 /* GDBM 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 #include "gdbm.h"
9 
10 #include <fcntl.h>
11 #include <stdlib.h>               // free()
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 
15 #if defined(WIN32) && !defined(__CYGWIN__)
16 #include "gdbmerrno.h"
17 extern const char * gdbm_strerror(gdbm_error);
18 #endif
19 
20 typedef struct {
21     PyTypeObject *gdbm_type;
22     PyObject *gdbm_error;
23 } _gdbm_state;
24 
25 static inline _gdbm_state*
get_gdbm_state(PyObject * module)26 get_gdbm_state(PyObject *module)
27 {
28     void *state = PyModule_GetState(module);
29     assert(state != NULL);
30     return (_gdbm_state *)state;
31 }
32 
33 /*[clinic input]
34 module _gdbm
35 class _gdbm.gdbm "gdbmobject *" "&Gdbmtype"
36 [clinic start generated code]*/
37 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=38ae71cedfc7172b]*/
38 
39 PyDoc_STRVAR(gdbmmodule__doc__,
40 "This module provides an interface to the GNU DBM (GDBM) library.\n\
41 \n\
42 This module is quite similar to the dbm module, but uses GDBM instead to\n\
43 provide some additional functionality.  Please note that the file formats\n\
44 created by GDBM and dbm are incompatible.\n\
45 \n\
46 GDBM objects behave like mappings (dictionaries), except that keys and\n\
47 values are always immutable bytes-like objects or strings.  Printing\n\
48 a GDBM object doesn't print the keys and values, and the items() and\n\
49 values() methods are not supported.");
50 
51 typedef struct {
52     PyObject_HEAD
53     Py_ssize_t di_size;        /* -1 means recompute */
54     GDBM_FILE di_dbm;
55 } gdbmobject;
56 
57 #include "clinic/_gdbmmodule.c.h"
58 
59 #define check_gdbmobject_open(v, err)                                 \
60     if ((v)->di_dbm == NULL) {                                       \
61         PyErr_SetString(err, "GDBM object has already been closed"); \
62         return NULL;                                                 \
63     }
64 
65 PyDoc_STRVAR(gdbm_object__doc__,
66 "This object represents a GDBM database.\n\
67 GDBM objects behave like mappings (dictionaries), except that keys and\n\
68 values are always immutable bytes-like objects or strings.  Printing\n\
69 a GDBM object doesn't print the keys and values, and the items() and\n\
70 values() methods are not supported.\n\
71 \n\
72 GDBM objects also support additional operations such as firstkey,\n\
73 nextkey, reorganize, and sync.");
74 
75 static PyObject *
newgdbmobject(_gdbm_state * state,const char * file,int flags,int mode)76 newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
77 {
78     gdbmobject *dp = PyObject_GC_New(gdbmobject, state->gdbm_type);
79     if (dp == NULL) {
80         return NULL;
81     }
82     dp->di_size = -1;
83     errno = 0;
84     PyObject_GC_Track(dp);
85 
86     if ((dp->di_dbm = gdbm_open((char *)file, 0, flags, mode, NULL)) == 0) {
87         if (errno != 0) {
88             PyErr_SetFromErrnoWithFilename(state->gdbm_error, file);
89         }
90         else {
91             PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
92         }
93         Py_DECREF(dp);
94         return NULL;
95     }
96     return (PyObject *)dp;
97 }
98 
99 /* Methods */
100 static int
gdbm_traverse(gdbmobject * dp,visitproc visit,void * arg)101 gdbm_traverse(gdbmobject *dp, visitproc visit, void *arg)
102 {
103     Py_VISIT(Py_TYPE(dp));
104     return 0;
105 }
106 
107 static void
gdbm_dealloc(gdbmobject * dp)108 gdbm_dealloc(gdbmobject *dp)
109 {
110     PyObject_GC_UnTrack(dp);
111     if (dp->di_dbm) {
112         gdbm_close(dp->di_dbm);
113     }
114     PyTypeObject *tp = Py_TYPE(dp);
115     tp->tp_free(dp);
116     Py_DECREF(tp);
117 }
118 
119 static Py_ssize_t
gdbm_length(gdbmobject * dp)120 gdbm_length(gdbmobject *dp)
121 {
122     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
123     if (dp->di_dbm == NULL) {
124         PyErr_SetString(state->gdbm_error, "GDBM object has already been closed");
125         return -1;
126     }
127     if (dp->di_size < 0) {
128 #if GDBM_VERSION_MAJOR >= 1 && GDBM_VERSION_MINOR >= 11
129         errno = 0;
130         gdbm_count_t count;
131         if (gdbm_count(dp->di_dbm, &count) == -1) {
132             if (errno != 0) {
133                 PyErr_SetFromErrno(state->gdbm_error);
134             }
135             else {
136                 PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
137             }
138             return -1;
139         }
140         if (count > PY_SSIZE_T_MAX) {
141             PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX");
142             return -1;
143         }
144         dp->di_size = count;
145 #else
146         datum key,okey;
147         okey.dsize=0;
148         okey.dptr=NULL;
149 
150         Py_ssize_t size = 0;
151         for (key = gdbm_firstkey(dp->di_dbm); key.dptr;
152              key = gdbm_nextkey(dp->di_dbm,okey)) {
153             size++;
154             if (okey.dsize) {
155                 free(okey.dptr);
156             }
157             okey=key;
158         }
159         dp->di_size = size;
160 #endif
161     }
162     return dp->di_size;
163 }
164 
165 // Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size).
166 // This function is needed to support PY_SSIZE_T_CLEAN.
167 // Return 1 on success, same to PyArg_Parse().
168 static int
parse_datum(PyObject * o,datum * d,const char * failmsg)169 parse_datum(PyObject *o, datum *d, const char *failmsg)
170 {
171     Py_ssize_t size;
172     if (!PyArg_Parse(o, "s#", &d->dptr, &size)) {
173         if (failmsg != NULL) {
174             PyErr_SetString(PyExc_TypeError, failmsg);
175         }
176         return 0;
177     }
178     if (INT_MAX < size) {
179         PyErr_SetString(PyExc_OverflowError, "size does not fit in an int");
180         return 0;
181     }
182     d->dsize = size;
183     return 1;
184 }
185 
186 static PyObject *
gdbm_subscript(gdbmobject * dp,PyObject * key)187 gdbm_subscript(gdbmobject *dp, PyObject *key)
188 {
189     PyObject *v;
190     datum drec, krec;
191     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
192 
193     if (!parse_datum(key, &krec, NULL)) {
194         return NULL;
195     }
196     if (dp->di_dbm == NULL) {
197         PyErr_SetString(state->gdbm_error,
198                         "GDBM object has already been closed");
199         return NULL;
200     }
201     drec = gdbm_fetch(dp->di_dbm, krec);
202     if (drec.dptr == 0) {
203         PyErr_SetObject(PyExc_KeyError, key);
204         return NULL;
205     }
206     v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
207     free(drec.dptr);
208     return v;
209 }
210 
211 /*[clinic input]
212 _gdbm.gdbm.get
213 
214     key: object
215     default: object = None
216     /
217 
218 Get the value for key, or default if not present.
219 [clinic start generated code]*/
220 
221 static PyObject *
_gdbm_gdbm_get_impl(gdbmobject * self,PyObject * key,PyObject * default_value)222 _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value)
223 /*[clinic end generated code: output=92421838f3a852f4 input=a9c20423f34c17b6]*/
224 {
225     PyObject *res;
226 
227     res = gdbm_subscript(self, key);
228     if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
229         PyErr_Clear();
230         Py_INCREF(default_value);
231         return default_value;
232     }
233     return res;
234 }
235 
236 static int
gdbm_ass_sub(gdbmobject * dp,PyObject * v,PyObject * w)237 gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
238 {
239     datum krec, drec;
240     const char *failmsg = "gdbm mappings have bytes or string indices only";
241     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
242 
243     if (!parse_datum(v, &krec, failmsg)) {
244         return -1;
245     }
246     if (dp->di_dbm == NULL) {
247         PyErr_SetString(state->gdbm_error,
248                         "GDBM object has already been closed");
249         return -1;
250     }
251     dp->di_size = -1;
252     if (w == NULL) {
253         if (gdbm_delete(dp->di_dbm, krec) < 0) {
254             if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
255                 PyErr_SetObject(PyExc_KeyError, v);
256             }
257             else {
258                 PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
259             }
260             return -1;
261         }
262     }
263     else {
264         if (!parse_datum(w, &drec, failmsg)) {
265             return -1;
266         }
267         errno = 0;
268         if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
269             if (errno != 0)
270                 PyErr_SetFromErrno(state->gdbm_error);
271             else
272                 PyErr_SetString(state->gdbm_error,
273                                 gdbm_strerror(gdbm_errno));
274             return -1;
275         }
276     }
277     return 0;
278 }
279 
280 /*[clinic input]
281 _gdbm.gdbm.setdefault
282 
283     key: object
284     default: object = None
285     /
286 
287 Get value for key, or set it to default and return default if not present.
288 [clinic start generated code]*/
289 
290 static PyObject *
_gdbm_gdbm_setdefault_impl(gdbmobject * self,PyObject * key,PyObject * default_value)291 _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
292                            PyObject *default_value)
293 /*[clinic end generated code: output=f3246e880509f142 input=0db46b69e9680171]*/
294 {
295     PyObject *res;
296 
297     res = gdbm_subscript(self, key);
298     if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
299         PyErr_Clear();
300         if (gdbm_ass_sub(self, key, default_value) < 0)
301             return NULL;
302         return gdbm_subscript(self, key);
303     }
304     return res;
305 }
306 
307 /*[clinic input]
308 _gdbm.gdbm.close
309 
310 Close the database.
311 [clinic start generated code]*/
312 
313 static PyObject *
_gdbm_gdbm_close_impl(gdbmobject * self)314 _gdbm_gdbm_close_impl(gdbmobject *self)
315 /*[clinic end generated code: output=f5abb4d6bb9e52d5 input=0a203447379b45fd]*/
316 {
317     if (self->di_dbm) {
318         gdbm_close(self->di_dbm);
319     }
320     self->di_dbm = NULL;
321     Py_RETURN_NONE;
322 }
323 
324 /* XXX Should return a set or a set view */
325 /*[clinic input]
326 _gdbm.gdbm.keys
327 
328     cls: defining_class
329 
330 Get a list of all keys in the database.
331 [clinic start generated code]*/
332 
333 static PyObject *
_gdbm_gdbm_keys_impl(gdbmobject * self,PyTypeObject * cls)334 _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls)
335 /*[clinic end generated code: output=c24b824e81404755 input=1428b7c79703d7d5]*/
336 {
337     PyObject *v, *item;
338     datum key, nextkey;
339     int err;
340 
341     _gdbm_state *state = PyType_GetModuleState(cls);
342     assert(state != NULL);
343 
344     if (self == NULL || !Py_IS_TYPE(self, state->gdbm_type)) {
345         PyErr_BadInternalCall();
346         return NULL;
347     }
348     check_gdbmobject_open(self, state->gdbm_error);
349 
350     v = PyList_New(0);
351     if (v == NULL)
352         return NULL;
353 
354     key = gdbm_firstkey(self->di_dbm);
355     while (key.dptr) {
356         item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
357         if (item == NULL) {
358             free(key.dptr);
359             Py_DECREF(v);
360             return NULL;
361         }
362         err = PyList_Append(v, item);
363         Py_DECREF(item);
364         if (err != 0) {
365             free(key.dptr);
366             Py_DECREF(v);
367             return NULL;
368         }
369         nextkey = gdbm_nextkey(self->di_dbm, key);
370         free(key.dptr);
371         key = nextkey;
372     }
373     return v;
374 }
375 
376 static int
gdbm_contains(PyObject * self,PyObject * arg)377 gdbm_contains(PyObject *self, PyObject *arg)
378 {
379     gdbmobject *dp = (gdbmobject *)self;
380     datum key;
381     Py_ssize_t size;
382     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
383 
384     if ((dp)->di_dbm == NULL) {
385         PyErr_SetString(state->gdbm_error,
386                         "GDBM object has already been closed");
387         return -1;
388     }
389     if (PyUnicode_Check(arg)) {
390         key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
391         key.dsize = size;
392         if (key.dptr == NULL)
393             return -1;
394     }
395     else if (!PyBytes_Check(arg)) {
396         PyErr_Format(PyExc_TypeError,
397                      "gdbm key must be bytes or string, not %.100s",
398                      Py_TYPE(arg)->tp_name);
399         return -1;
400     }
401     else {
402         key.dptr = PyBytes_AS_STRING(arg);
403         key.dsize = PyBytes_GET_SIZE(arg);
404     }
405     return gdbm_exists(dp->di_dbm, key);
406 }
407 
408 /*[clinic input]
409 _gdbm.gdbm.firstkey
410 
411     cls: defining_class
412 
413 Return the starting key for the traversal.
414 
415 It's possible to loop over every key in the database using this method
416 and the nextkey() method.  The traversal is ordered by GDBM's internal
417 hash values, and won't be sorted by the key values.
418 [clinic start generated code]*/
419 
420 static PyObject *
_gdbm_gdbm_firstkey_impl(gdbmobject * self,PyTypeObject * cls)421 _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls)
422 /*[clinic end generated code: output=139275e9c8b60827 input=ed8782a029a5d299]*/
423 {
424     PyObject *v;
425     datum key;
426     _gdbm_state *state = PyType_GetModuleState(cls);
427     assert(state != NULL);
428 
429     check_gdbmobject_open(self, state->gdbm_error);
430     key = gdbm_firstkey(self->di_dbm);
431     if (key.dptr) {
432         v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
433         free(key.dptr);
434         return v;
435     }
436     else {
437         Py_RETURN_NONE;
438     }
439 }
440 
441 /*[clinic input]
442 _gdbm.gdbm.nextkey
443 
444     cls: defining_class
445     key: str(accept={str, robuffer}, zeroes=True)
446     /
447 
448 Returns the key that follows key in the traversal.
449 
450 The following code prints every key in the database db, without having
451 to create a list in memory that contains them all:
452 
453       k = db.firstkey()
454       while k is not None:
455           print(k)
456           k = db.nextkey(k)
457 [clinic start generated code]*/
458 
459 static PyObject *
_gdbm_gdbm_nextkey_impl(gdbmobject * self,PyTypeObject * cls,const char * key,Py_ssize_t key_length)460 _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key,
461                         Py_ssize_t key_length)
462 /*[clinic end generated code: output=c81a69300ef41766 input=365e297bc0b3db48]*/
463 {
464     PyObject *v;
465     datum dbm_key, nextkey;
466     _gdbm_state *state = PyType_GetModuleState(cls);
467     assert(state != NULL);
468 
469     dbm_key.dptr = (char *)key;
470     dbm_key.dsize = key_length;
471     check_gdbmobject_open(self, state->gdbm_error);
472     nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
473     if (nextkey.dptr) {
474         v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
475         free(nextkey.dptr);
476         return v;
477     }
478     else {
479         Py_RETURN_NONE;
480     }
481 }
482 
483 /*[clinic input]
484 _gdbm.gdbm.reorganize
485 
486     cls: defining_class
487 
488 Reorganize the database.
489 
490 If you have carried out a lot of deletions and would like to shrink
491 the space used by the GDBM file, this routine will reorganize the
492 database.  GDBM will not shorten the length of a database file except
493 by using this reorganization; otherwise, deleted file space will be
494 kept and reused as new (key,value) pairs are added.
495 [clinic start generated code]*/
496 
497 static PyObject *
_gdbm_gdbm_reorganize_impl(gdbmobject * self,PyTypeObject * cls)498 _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls)
499 /*[clinic end generated code: output=d77c69e8e3dd644a input=e1359faeef844e46]*/
500 {
501     _gdbm_state *state = PyType_GetModuleState(cls);
502     assert(state != NULL);
503     check_gdbmobject_open(self, state->gdbm_error);
504     errno = 0;
505     if (gdbm_reorganize(self->di_dbm) < 0) {
506         if (errno != 0)
507             PyErr_SetFromErrno(state->gdbm_error);
508         else
509             PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
510         return NULL;
511     }
512     Py_RETURN_NONE;
513 }
514 
515 /*[clinic input]
516 _gdbm.gdbm.sync
517 
518     cls: defining_class
519 
520 Flush the database to the disk file.
521 
522 When the database has been opened in fast mode, this method forces
523 any unwritten data to be written to the disk.
524 [clinic start generated code]*/
525 
526 static PyObject *
_gdbm_gdbm_sync_impl(gdbmobject * self,PyTypeObject * cls)527 _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls)
528 /*[clinic end generated code: output=bb680a2035c3f592 input=3d749235f79b6f2a]*/
529 {
530     _gdbm_state *state = PyType_GetModuleState(cls);
531     assert(state != NULL);
532     check_gdbmobject_open(self, state->gdbm_error);
533     gdbm_sync(self->di_dbm);
534     Py_RETURN_NONE;
535 }
536 
537 static PyObject *
gdbm__enter__(PyObject * self,PyObject * args)538 gdbm__enter__(PyObject *self, PyObject *args)
539 {
540     Py_INCREF(self);
541     return self;
542 }
543 
544 static PyObject *
gdbm__exit__(PyObject * self,PyObject * args)545 gdbm__exit__(PyObject *self, PyObject *args)
546 {
547     return _gdbm_gdbm_close_impl((gdbmobject *)self);
548 }
549 
550 static PyMethodDef gdbm_methods[] = {
551     _GDBM_GDBM_CLOSE_METHODDEF
552     _GDBM_GDBM_KEYS_METHODDEF
553     _GDBM_GDBM_FIRSTKEY_METHODDEF
554     _GDBM_GDBM_NEXTKEY_METHODDEF
555     _GDBM_GDBM_REORGANIZE_METHODDEF
556     _GDBM_GDBM_SYNC_METHODDEF
557     _GDBM_GDBM_GET_METHODDEF
558     _GDBM_GDBM_SETDEFAULT_METHODDEF
559     {"__enter__", gdbm__enter__, METH_NOARGS, NULL},
560     {"__exit__",  gdbm__exit__, METH_VARARGS, NULL},
561     {NULL,              NULL}           /* sentinel */
562 };
563 
564 static PyType_Slot gdbmtype_spec_slots[] = {
565     {Py_tp_dealloc, gdbm_dealloc},
566     {Py_tp_traverse, gdbm_traverse},
567     {Py_tp_methods, gdbm_methods},
568     {Py_sq_contains, gdbm_contains},
569     {Py_mp_length, gdbm_length},
570     {Py_mp_subscript, gdbm_subscript},
571     {Py_mp_ass_subscript, gdbm_ass_sub},
572     {Py_tp_doc, (char*)gdbm_object__doc__},
573     {0, 0}
574 };
575 
576 static PyType_Spec gdbmtype_spec = {
577     .name = "_gdbm.gdbm",
578     .basicsize = sizeof(gdbmobject),
579     // Calling PyType_GetModuleState() on a subclass is not safe.
580     // dbmtype_spec does not have Py_TPFLAGS_BASETYPE flag
581     // which prevents to create a subclass.
582     // So calling PyType_GetModuleState() in this file is always safe.
583     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
584               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
585     .slots = gdbmtype_spec_slots,
586 };
587 
588 /* ----------------------------------------------------------------- */
589 
590 /*[clinic input]
591 _gdbm.open as dbmopen
592 
593     filename: object
594     flags: str="r"
595     mode: int(py_default="0o666") = 0o666
596     /
597 
598 Open a dbm database and return a dbm object.
599 
600 The filename argument is the name of the database file.
601 
602 The optional flags argument can be 'r' (to open an existing database
603 for reading only -- default), 'w' (to open an existing database for
604 reading and writing), 'c' (which creates the database if it doesn't
605 exist), or 'n' (which always creates a new empty database).
606 
607 Some versions of gdbm support additional flags which must be
608 appended to one of the flags described above.  The module constant
609 'open_flags' is a string of valid additional flags.  The 'f' flag
610 opens the database in fast mode; altered data will not automatically
611 be written to the disk after every change.  This results in faster
612 writes to the database, but may result in an inconsistent database
613 if the program crashes while the database is still open.  Use the
614 sync() method to force any unwritten data to be written to the disk.
615 The 's' flag causes all database operations to be synchronized to
616 disk.  The 'u' flag disables locking of the database file.
617 
618 The optional mode argument is the Unix mode of the file, used only
619 when the database has to be created.  It defaults to octal 0o666.
620 [clinic start generated code]*/
621 
622 static PyObject *
dbmopen_impl(PyObject * module,PyObject * filename,const char * flags,int mode)623 dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
624              int mode)
625 /*[clinic end generated code: output=9527750f5df90764 input=bca6ec81dc49292c]*/
626 {
627     int iflags;
628     _gdbm_state *state = get_gdbm_state(module);
629     assert(state != NULL);
630 
631     switch (flags[0]) {
632     case 'r':
633         iflags = GDBM_READER;
634         break;
635     case 'w':
636         iflags = GDBM_WRITER;
637         break;
638     case 'c':
639         iflags = GDBM_WRCREAT;
640         break;
641     case 'n':
642         iflags = GDBM_NEWDB;
643         break;
644     default:
645         PyErr_SetString(state->gdbm_error,
646                         "First flag must be one of 'r', 'w', 'c' or 'n'");
647         return NULL;
648     }
649     for (flags++; *flags != '\0'; flags++) {
650         char buf[40];
651         switch (*flags) {
652 #ifdef GDBM_FAST
653             case 'f':
654                 iflags |= GDBM_FAST;
655                 break;
656 #endif
657 #ifdef GDBM_SYNC
658             case 's':
659                 iflags |= GDBM_SYNC;
660                 break;
661 #endif
662 #ifdef GDBM_NOLOCK
663             case 'u':
664                 iflags |= GDBM_NOLOCK;
665                 break;
666 #endif
667             default:
668                 PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
669                               *flags);
670                 PyErr_SetString(state->gdbm_error, buf);
671                 return NULL;
672         }
673     }
674 
675     PyObject *filenamebytes;
676     if (!PyUnicode_FSConverter(filename, &filenamebytes)) {
677         return NULL;
678     }
679 
680     const char *name = PyBytes_AS_STRING(filenamebytes);
681     if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
682         Py_DECREF(filenamebytes);
683         PyErr_SetString(PyExc_ValueError, "embedded null character");
684         return NULL;
685     }
686     PyObject *self = newgdbmobject(state, name, iflags, mode);
687     Py_DECREF(filenamebytes);
688     return self;
689 }
690 
691 static const char gdbmmodule_open_flags[] = "rwcn"
692 #ifdef GDBM_FAST
693                                      "f"
694 #endif
695 #ifdef GDBM_SYNC
696                                      "s"
697 #endif
698 #ifdef GDBM_NOLOCK
699                                      "u"
700 #endif
701                                      ;
702 
703 static PyMethodDef _gdbm_module_methods[] = {
704     DBMOPEN_METHODDEF
705     { 0, 0 },
706 };
707 
708 static int
_gdbm_exec(PyObject * module)709 _gdbm_exec(PyObject *module)
710 {
711     _gdbm_state *state = get_gdbm_state(module);
712     state->gdbm_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
713                                                         &gdbmtype_spec, NULL);
714     if (state->gdbm_type == NULL) {
715         return -1;
716     }
717     state->gdbm_error = PyErr_NewException("_gdbm.error", PyExc_OSError, NULL);
718     if (state->gdbm_error == NULL) {
719         return -1;
720     }
721     if (PyModule_AddType(module, (PyTypeObject *)state->gdbm_error) < 0) {
722         return -1;
723     }
724     if (PyModule_AddStringConstant(module, "open_flags",
725                                    gdbmmodule_open_flags) < 0) {
726         return -1;
727     }
728 
729 #if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
730     defined(GDBM_VERSION_PATCH)
731     PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
732                                   GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
733     if (obj == NULL) {
734         return -1;
735     }
736     if (PyModule_AddObject(module, "_GDBM_VERSION", obj) < 0) {
737         Py_DECREF(obj);
738         return -1;
739     }
740 #endif
741     return 0;
742 }
743 
744 static int
_gdbm_module_traverse(PyObject * module,visitproc visit,void * arg)745 _gdbm_module_traverse(PyObject *module, visitproc visit, void *arg)
746 {
747     _gdbm_state *state = get_gdbm_state(module);
748     Py_VISIT(state->gdbm_error);
749     Py_VISIT(state->gdbm_type);
750     return 0;
751 }
752 
753 static int
_gdbm_module_clear(PyObject * module)754 _gdbm_module_clear(PyObject *module)
755 {
756     _gdbm_state *state = get_gdbm_state(module);
757     Py_CLEAR(state->gdbm_error);
758     Py_CLEAR(state->gdbm_type);
759     return 0;
760 }
761 
762 static void
_gdbm_module_free(void * module)763 _gdbm_module_free(void *module)
764 {
765     _gdbm_module_clear((PyObject *)module);
766 }
767 
768 static PyModuleDef_Slot _gdbm_module_slots[] = {
769     {Py_mod_exec, _gdbm_exec},
770     {0, NULL}
771 };
772 
773 static struct PyModuleDef _gdbmmodule = {
774     PyModuleDef_HEAD_INIT,
775     .m_name = "_gdbm",
776     .m_doc = gdbmmodule__doc__,
777     .m_size = sizeof(_gdbm_state),
778     .m_methods = _gdbm_module_methods,
779     .m_slots = _gdbm_module_slots,
780     .m_traverse = _gdbm_module_traverse,
781     .m_clear = _gdbm_module_clear,
782     .m_free = _gdbm_module_free,
783 };
784 
785 PyMODINIT_FUNC
PyInit__gdbm(void)786 PyInit__gdbm(void)
787 {
788     return PyModuleDef_Init(&_gdbmmodule);
789 }
790