• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 typedef struct {
20     PyTypeObject *gdbm_type;
21     PyObject *gdbm_error;
22 } _gdbm_state;
23 
24 static inline _gdbm_state*
get_gdbm_state(PyObject * module)25 get_gdbm_state(PyObject *module)
26 {
27     void *state = PyModule_GetState(module);
28     assert(state != NULL);
29     return (_gdbm_state *)state;
30 }
31 
32 /*[clinic input]
33 module _gdbm
34 class _gdbm.gdbm "gdbmobject *" "&Gdbmtype"
35 [clinic start generated code]*/
36 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=38ae71cedfc7172b]*/
37 
38 PyDoc_STRVAR(gdbmmodule__doc__,
39 "This module provides an interface to the GNU DBM (GDBM) library.\n\
40 \n\
41 This module is quite similar to the dbm module, but uses GDBM instead to\n\
42 provide some additional functionality.  Please note that the file formats\n\
43 created by GDBM and dbm are incompatible.\n\
44 \n\
45 GDBM objects behave like mappings (dictionaries), except that keys and\n\
46 values are always immutable bytes-like objects or strings.  Printing\n\
47 a GDBM object doesn't print the keys and values, and the items() and\n\
48 values() methods are not supported.");
49 
50 typedef struct {
51     PyObject_HEAD
52     Py_ssize_t di_size;        /* -1 means recompute */
53     GDBM_FILE di_dbm;
54 } gdbmobject;
55 
56 #include "clinic/_gdbmmodule.c.h"
57 
58 #define check_gdbmobject_open(v, err)                                 \
59     if ((v)->di_dbm == NULL) {                                       \
60         PyErr_SetString(err, "GDBM object has already been closed"); \
61         return NULL;                                                 \
62     }
63 
64 PyDoc_STRVAR(gdbm_object__doc__,
65 "This object represents a GDBM database.\n\
66 GDBM objects behave like mappings (dictionaries), except that keys and\n\
67 values are always immutable bytes-like objects or strings.  Printing\n\
68 a GDBM object doesn't print the keys and values, and the items() and\n\
69 values() methods are not supported.\n\
70 \n\
71 GDBM objects also support additional operations such as firstkey,\n\
72 nextkey, reorganize, and sync.");
73 
74 static PyObject *
newgdbmobject(_gdbm_state * state,const char * file,int flags,int mode)75 newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
76 {
77     gdbmobject *dp = PyObject_GC_New(gdbmobject, state->gdbm_type);
78     if (dp == NULL) {
79         return NULL;
80     }
81     dp->di_size = -1;
82     errno = 0;
83     PyObject_GC_Track(dp);
84 
85     if ((dp->di_dbm = gdbm_open((char *)file, 0, flags, mode, NULL)) == 0) {
86         if (errno != 0) {
87             PyErr_SetFromErrnoWithFilename(state->gdbm_error, file);
88         }
89         else {
90             PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
91         }
92         Py_DECREF(dp);
93         return NULL;
94     }
95     return (PyObject *)dp;
96 }
97 
98 /* Methods */
99 static int
gdbm_traverse(gdbmobject * dp,visitproc visit,void * arg)100 gdbm_traverse(gdbmobject *dp, visitproc visit, void *arg)
101 {
102     Py_VISIT(Py_TYPE(dp));
103     return 0;
104 }
105 
106 static void
gdbm_dealloc(gdbmobject * dp)107 gdbm_dealloc(gdbmobject *dp)
108 {
109     PyObject_GC_UnTrack(dp);
110     if (dp->di_dbm) {
111         gdbm_close(dp->di_dbm);
112     }
113     PyTypeObject *tp = Py_TYPE(dp);
114     tp->tp_free(dp);
115     Py_DECREF(tp);
116 }
117 
118 static Py_ssize_t
gdbm_length(gdbmobject * dp)119 gdbm_length(gdbmobject *dp)
120 {
121     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
122     if (dp->di_dbm == NULL) {
123         PyErr_SetString(state->gdbm_error, "GDBM object has already been closed");
124         return -1;
125     }
126     if (dp->di_size < 0) {
127 #if GDBM_VERSION_MAJOR >= 1 && GDBM_VERSION_MINOR >= 11
128         errno = 0;
129         gdbm_count_t count;
130         if (gdbm_count(dp->di_dbm, &count) == -1) {
131             if (errno != 0) {
132                 PyErr_SetFromErrno(state->gdbm_error);
133             }
134             else {
135                 PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
136             }
137             return -1;
138         }
139         if (count > PY_SSIZE_T_MAX) {
140             PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX");
141             return -1;
142         }
143         dp->di_size = count;
144 #else
145         datum key,okey;
146         okey.dsize=0;
147         okey.dptr=NULL;
148 
149         Py_ssize_t size = 0;
150         for (key = gdbm_firstkey(dp->di_dbm); key.dptr;
151              key = gdbm_nextkey(dp->di_dbm,okey)) {
152             size++;
153             if (okey.dsize) {
154                 free(okey.dptr);
155             }
156             okey=key;
157         }
158         dp->di_size = size;
159 #endif
160     }
161     return dp->di_size;
162 }
163 
164 // Wrapper function for PyArg_Parse(o, "s#", &d.dptr, &d.size).
165 // This function is needed to support PY_SSIZE_T_CLEAN.
166 // Return 1 on success, same to PyArg_Parse().
167 static int
parse_datum(PyObject * o,datum * d,const char * failmsg)168 parse_datum(PyObject *o, datum *d, const char *failmsg)
169 {
170     Py_ssize_t size;
171     if (!PyArg_Parse(o, "s#", &d->dptr, &size)) {
172         if (failmsg != NULL) {
173             PyErr_SetString(PyExc_TypeError, failmsg);
174         }
175         return 0;
176     }
177     if (INT_MAX < size) {
178         PyErr_SetString(PyExc_OverflowError, "size does not fit in an int");
179         return 0;
180     }
181     d->dsize = size;
182     return 1;
183 }
184 
185 static PyObject *
gdbm_subscript(gdbmobject * dp,PyObject * key)186 gdbm_subscript(gdbmobject *dp, PyObject *key)
187 {
188     PyObject *v;
189     datum drec, krec;
190     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
191 
192     if (!parse_datum(key, &krec, NULL)) {
193         return NULL;
194     }
195     if (dp->di_dbm == NULL) {
196         PyErr_SetString(state->gdbm_error,
197                         "GDBM object has already been closed");
198         return NULL;
199     }
200     drec = gdbm_fetch(dp->di_dbm, krec);
201     if (drec.dptr == 0) {
202         PyErr_SetObject(PyExc_KeyError, key);
203         return NULL;
204     }
205     v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
206     free(drec.dptr);
207     return v;
208 }
209 
210 /*[clinic input]
211 _gdbm.gdbm.get
212 
213     key: object
214     default: object = None
215     /
216 
217 Get the value for key, or default if not present.
218 [clinic start generated code]*/
219 
220 static PyObject *
_gdbm_gdbm_get_impl(gdbmobject * self,PyObject * key,PyObject * default_value)221 _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value)
222 /*[clinic end generated code: output=92421838f3a852f4 input=a9c20423f34c17b6]*/
223 {
224     PyObject *res;
225 
226     res = gdbm_subscript(self, key);
227     if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
228         PyErr_Clear();
229         Py_INCREF(default_value);
230         return default_value;
231     }
232     return res;
233 }
234 
235 static int
gdbm_ass_sub(gdbmobject * dp,PyObject * v,PyObject * w)236 gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
237 {
238     datum krec, drec;
239     const char *failmsg = "gdbm mappings have bytes or string indices only";
240     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
241 
242     if (!parse_datum(v, &krec, failmsg)) {
243         return -1;
244     }
245     if (dp->di_dbm == NULL) {
246         PyErr_SetString(state->gdbm_error,
247                         "GDBM object has already been closed");
248         return -1;
249     }
250     dp->di_size = -1;
251     if (w == NULL) {
252         if (gdbm_delete(dp->di_dbm, krec) < 0) {
253             if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
254                 PyErr_SetObject(PyExc_KeyError, v);
255             }
256             else {
257                 PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
258             }
259             return -1;
260         }
261     }
262     else {
263         if (!parse_datum(w, &drec, failmsg)) {
264             return -1;
265         }
266         errno = 0;
267         if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
268             if (errno != 0)
269                 PyErr_SetFromErrno(state->gdbm_error);
270             else
271                 PyErr_SetString(state->gdbm_error,
272                                 gdbm_strerror(gdbm_errno));
273             return -1;
274         }
275     }
276     return 0;
277 }
278 
279 /*[clinic input]
280 _gdbm.gdbm.setdefault
281 
282     key: object
283     default: object = None
284     /
285 
286 Get value for key, or set it to default and return default if not present.
287 [clinic start generated code]*/
288 
289 static PyObject *
_gdbm_gdbm_setdefault_impl(gdbmobject * self,PyObject * key,PyObject * default_value)290 _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
291                            PyObject *default_value)
292 /*[clinic end generated code: output=f3246e880509f142 input=0db46b69e9680171]*/
293 {
294     PyObject *res;
295 
296     res = gdbm_subscript(self, key);
297     if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
298         PyErr_Clear();
299         if (gdbm_ass_sub(self, key, default_value) < 0)
300             return NULL;
301         return gdbm_subscript(self, key);
302     }
303     return res;
304 }
305 
306 /*[clinic input]
307 _gdbm.gdbm.close
308 
309 Close the database.
310 [clinic start generated code]*/
311 
312 static PyObject *
_gdbm_gdbm_close_impl(gdbmobject * self)313 _gdbm_gdbm_close_impl(gdbmobject *self)
314 /*[clinic end generated code: output=f5abb4d6bb9e52d5 input=0a203447379b45fd]*/
315 {
316     if (self->di_dbm) {
317         gdbm_close(self->di_dbm);
318     }
319     self->di_dbm = NULL;
320     Py_RETURN_NONE;
321 }
322 
323 /* XXX Should return a set or a set view */
324 /*[clinic input]
325 _gdbm.gdbm.keys
326 
327     cls: defining_class
328 
329 Get a list of all keys in the database.
330 [clinic start generated code]*/
331 
332 static PyObject *
_gdbm_gdbm_keys_impl(gdbmobject * self,PyTypeObject * cls)333 _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls)
334 /*[clinic end generated code: output=c24b824e81404755 input=1428b7c79703d7d5]*/
335 {
336     PyObject *v, *item;
337     datum key, nextkey;
338     int err;
339 
340     _gdbm_state *state = PyType_GetModuleState(cls);
341     assert(state != NULL);
342 
343     if (self == NULL || !Py_IS_TYPE(self, state->gdbm_type)) {
344         PyErr_BadInternalCall();
345         return NULL;
346     }
347     check_gdbmobject_open(self, state->gdbm_error);
348 
349     v = PyList_New(0);
350     if (v == NULL)
351         return NULL;
352 
353     key = gdbm_firstkey(self->di_dbm);
354     while (key.dptr) {
355         item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
356         if (item == NULL) {
357             free(key.dptr);
358             Py_DECREF(v);
359             return NULL;
360         }
361         err = PyList_Append(v, item);
362         Py_DECREF(item);
363         if (err != 0) {
364             free(key.dptr);
365             Py_DECREF(v);
366             return NULL;
367         }
368         nextkey = gdbm_nextkey(self->di_dbm, key);
369         free(key.dptr);
370         key = nextkey;
371     }
372     return v;
373 }
374 
375 static int
gdbm_contains(PyObject * self,PyObject * arg)376 gdbm_contains(PyObject *self, PyObject *arg)
377 {
378     gdbmobject *dp = (gdbmobject *)self;
379     datum key;
380     Py_ssize_t size;
381     _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
382 
383     if ((dp)->di_dbm == NULL) {
384         PyErr_SetString(state->gdbm_error,
385                         "GDBM object has already been closed");
386         return -1;
387     }
388     if (PyUnicode_Check(arg)) {
389         key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
390         key.dsize = size;
391         if (key.dptr == NULL)
392             return -1;
393     }
394     else if (!PyBytes_Check(arg)) {
395         PyErr_Format(PyExc_TypeError,
396                      "gdbm key must be bytes or string, not %.100s",
397                      Py_TYPE(arg)->tp_name);
398         return -1;
399     }
400     else {
401         key.dptr = PyBytes_AS_STRING(arg);
402         key.dsize = PyBytes_GET_SIZE(arg);
403     }
404     return gdbm_exists(dp->di_dbm, key);
405 }
406 
407 /*[clinic input]
408 _gdbm.gdbm.firstkey
409 
410     cls: defining_class
411 
412 Return the starting key for the traversal.
413 
414 It's possible to loop over every key in the database using this method
415 and the nextkey() method.  The traversal is ordered by GDBM's internal
416 hash values, and won't be sorted by the key values.
417 [clinic start generated code]*/
418 
419 static PyObject *
_gdbm_gdbm_firstkey_impl(gdbmobject * self,PyTypeObject * cls)420 _gdbm_gdbm_firstkey_impl(gdbmobject *self, PyTypeObject *cls)
421 /*[clinic end generated code: output=139275e9c8b60827 input=ed8782a029a5d299]*/
422 {
423     PyObject *v;
424     datum key;
425     _gdbm_state *state = PyType_GetModuleState(cls);
426     assert(state != NULL);
427 
428     check_gdbmobject_open(self, state->gdbm_error);
429     key = gdbm_firstkey(self->di_dbm);
430     if (key.dptr) {
431         v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
432         free(key.dptr);
433         return v;
434     }
435     else {
436         Py_RETURN_NONE;
437     }
438 }
439 
440 /*[clinic input]
441 _gdbm.gdbm.nextkey
442 
443     cls: defining_class
444     key: str(accept={str, robuffer}, zeroes=True)
445     /
446 
447 Returns the key that follows key in the traversal.
448 
449 The following code prints every key in the database db, without having
450 to create a list in memory that contains them all:
451 
452       k = db.firstkey()
453       while k is not None:
454           print(k)
455           k = db.nextkey(k)
456 [clinic start generated code]*/
457 
458 static PyObject *
_gdbm_gdbm_nextkey_impl(gdbmobject * self,PyTypeObject * cls,const char * key,Py_ssize_clean_t key_length)459 _gdbm_gdbm_nextkey_impl(gdbmobject *self, PyTypeObject *cls, const char *key,
460                         Py_ssize_clean_t key_length)
461 /*[clinic end generated code: output=204964441fdbaf02 input=365e297bc0b3db48]*/
462 {
463     PyObject *v;
464     datum dbm_key, nextkey;
465     _gdbm_state *state = PyType_GetModuleState(cls);
466     assert(state != NULL);
467 
468     dbm_key.dptr = (char *)key;
469     dbm_key.dsize = key_length;
470     check_gdbmobject_open(self, state->gdbm_error);
471     nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
472     if (nextkey.dptr) {
473         v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
474         free(nextkey.dptr);
475         return v;
476     }
477     else {
478         Py_RETURN_NONE;
479     }
480 }
481 
482 /*[clinic input]
483 _gdbm.gdbm.reorganize
484 
485     cls: defining_class
486 
487 Reorganize the database.
488 
489 If you have carried out a lot of deletions and would like to shrink
490 the space used by the GDBM file, this routine will reorganize the
491 database.  GDBM will not shorten the length of a database file except
492 by using this reorganization; otherwise, deleted file space will be
493 kept and reused as new (key,value) pairs are added.
494 [clinic start generated code]*/
495 
496 static PyObject *
_gdbm_gdbm_reorganize_impl(gdbmobject * self,PyTypeObject * cls)497 _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls)
498 /*[clinic end generated code: output=d77c69e8e3dd644a input=e1359faeef844e46]*/
499 {
500     _gdbm_state *state = PyType_GetModuleState(cls);
501     assert(state != NULL);
502     check_gdbmobject_open(self, state->gdbm_error);
503     errno = 0;
504     if (gdbm_reorganize(self->di_dbm) < 0) {
505         if (errno != 0)
506             PyErr_SetFromErrno(state->gdbm_error);
507         else
508             PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
509         return NULL;
510     }
511     Py_RETURN_NONE;
512 }
513 
514 /*[clinic input]
515 _gdbm.gdbm.sync
516 
517     cls: defining_class
518 
519 Flush the database to the disk file.
520 
521 When the database has been opened in fast mode, this method forces
522 any unwritten data to be written to the disk.
523 [clinic start generated code]*/
524 
525 static PyObject *
_gdbm_gdbm_sync_impl(gdbmobject * self,PyTypeObject * cls)526 _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls)
527 /*[clinic end generated code: output=bb680a2035c3f592 input=3d749235f79b6f2a]*/
528 {
529     _gdbm_state *state = PyType_GetModuleState(cls);
530     assert(state != NULL);
531     check_gdbmobject_open(self, state->gdbm_error);
532     gdbm_sync(self->di_dbm);
533     Py_RETURN_NONE;
534 }
535 
536 static PyObject *
gdbm__enter__(PyObject * self,PyObject * args)537 gdbm__enter__(PyObject *self, PyObject *args)
538 {
539     Py_INCREF(self);
540     return self;
541 }
542 
543 static PyObject *
gdbm__exit__(PyObject * self,PyObject * args)544 gdbm__exit__(PyObject *self, PyObject *args)
545 {
546     _Py_IDENTIFIER(close);
547     return _PyObject_CallMethodIdNoArgs(self, &PyId_close);
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: unicode
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=812b7d74399ceb0e]*/
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 = PyUnicode_EncodeFSDefault(filename);
676     if (filenamebytes == NULL) {
677         return NULL;
678     }
679     const char *name = PyBytes_AS_STRING(filenamebytes);
680     if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
681         Py_DECREF(filenamebytes);
682         PyErr_SetString(PyExc_ValueError, "embedded null character");
683         return NULL;
684     }
685     PyObject *self = newgdbmobject(state, name, iflags, mode);
686     Py_DECREF(filenamebytes);
687     return self;
688 }
689 
690 static const char gdbmmodule_open_flags[] = "rwcn"
691 #ifdef GDBM_FAST
692                                      "f"
693 #endif
694 #ifdef GDBM_SYNC
695                                      "s"
696 #endif
697 #ifdef GDBM_NOLOCK
698                                      "u"
699 #endif
700                                      ;
701 
702 static PyMethodDef _gdbm_module_methods[] = {
703     DBMOPEN_METHODDEF
704     { 0, 0 },
705 };
706 
707 static int
_gdbm_exec(PyObject * module)708 _gdbm_exec(PyObject *module)
709 {
710     _gdbm_state *state = get_gdbm_state(module);
711     state->gdbm_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
712                                                         &gdbmtype_spec, NULL);
713     if (state->gdbm_type == NULL) {
714         return -1;
715     }
716     state->gdbm_error = PyErr_NewException("_gdbm.error", PyExc_OSError, NULL);
717     if (state->gdbm_error == NULL) {
718         return -1;
719     }
720     if (PyModule_AddType(module, (PyTypeObject *)state->gdbm_error) < 0) {
721         return -1;
722     }
723     if (PyModule_AddStringConstant(module, "open_flags",
724                                    gdbmmodule_open_flags) < 0) {
725         return -1;
726     }
727 
728 #if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
729     defined(GDBM_VERSION_PATCH)
730     PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
731                                   GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
732     if (obj == NULL) {
733         return -1;
734     }
735     if (PyModule_AddObject(module, "_GDBM_VERSION", obj) < 0) {
736         Py_DECREF(obj);
737         return -1;
738     }
739 #endif
740     return 0;
741 }
742 
743 static int
_gdbm_module_traverse(PyObject * module,visitproc visit,void * arg)744 _gdbm_module_traverse(PyObject *module, visitproc visit, void *arg)
745 {
746     _gdbm_state *state = get_gdbm_state(module);
747     Py_VISIT(state->gdbm_error);
748     Py_VISIT(state->gdbm_type);
749     return 0;
750 }
751 
752 static int
_gdbm_module_clear(PyObject * module)753 _gdbm_module_clear(PyObject *module)
754 {
755     _gdbm_state *state = get_gdbm_state(module);
756     Py_CLEAR(state->gdbm_error);
757     Py_CLEAR(state->gdbm_type);
758     return 0;
759 }
760 
761 static void
_gdbm_module_free(void * module)762 _gdbm_module_free(void *module)
763 {
764     _gdbm_module_clear((PyObject *)module);
765 }
766 
767 static PyModuleDef_Slot _gdbm_module_slots[] = {
768     {Py_mod_exec, _gdbm_exec},
769     {0, NULL}
770 };
771 
772 static struct PyModuleDef _gdbmmodule = {
773     PyModuleDef_HEAD_INIT,
774     .m_name = "_gdbm",
775     .m_doc = gdbmmodule__doc__,
776     .m_size = sizeof(_gdbm_state),
777     .m_methods = _gdbm_module_methods,
778     .m_slots = _gdbm_module_slots,
779     .m_traverse = _gdbm_module_traverse,
780     .m_clear = _gdbm_module_clear,
781     .m_free = _gdbm_module_free,
782 };
783 
784 PyMODINIT_FUNC
PyInit__gdbm(void)785 PyInit__gdbm(void)
786 {
787     return PyModuleDef_Init(&_gdbmmodule);
788 }
789