Branch data Line data Source code
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*
26 : 380 : get_gdbm_state(PyObject *module)
27 : : {
28 : 380 : void *state = PyModule_GetState(module);
29 : : assert(state != NULL);
30 : 380 : 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 *
76 : 215 : newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
77 : : {
78 : 215 : gdbmobject *dp = PyObject_GC_New(gdbmobject, state->gdbm_type);
79 [ - + ]: 215 : if (dp == NULL) {
80 : 0 : return NULL;
81 : : }
82 : 215 : dp->di_size = -1;
83 : 215 : errno = 0;
84 : 215 : PyObject_GC_Track(dp);
85 : :
86 [ + + ]: 215 : if ((dp->di_dbm = gdbm_open((char *)file, 0, flags, mode, NULL)) == 0) {
87 [ + - ]: 2 : if (errno != 0) {
88 : 2 : PyErr_SetFromErrnoWithFilename(state->gdbm_error, file);
89 : : }
90 : : else {
91 : 0 : PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
92 : : }
93 : 2 : Py_DECREF(dp);
94 : 2 : return NULL;
95 : : }
96 : 213 : return (PyObject *)dp;
97 : : }
98 : :
99 : : /* Methods */
100 : : static int
101 : 26 : gdbm_traverse(gdbmobject *dp, visitproc visit, void *arg)
102 : : {
103 [ + - - + ]: 26 : Py_VISIT(Py_TYPE(dp));
104 : 26 : return 0;
105 : : }
106 : :
107 : : static void
108 : 215 : gdbm_dealloc(gdbmobject *dp)
109 : : {
110 : 215 : PyObject_GC_UnTrack(dp);
111 [ - + ]: 215 : if (dp->di_dbm) {
112 : 0 : gdbm_close(dp->di_dbm);
113 : : }
114 : 215 : PyTypeObject *tp = Py_TYPE(dp);
115 : 215 : tp->tp_free(dp);
116 : 215 : Py_DECREF(tp);
117 : 215 : }
118 : :
119 : : static Py_ssize_t
120 : 121 : gdbm_length(gdbmobject *dp)
121 : : {
122 : 121 : _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
123 [ - + ]: 121 : if (dp->di_dbm == NULL) {
124 : 0 : PyErr_SetString(state->gdbm_error, "GDBM object has already been closed");
125 : 0 : return -1;
126 : : }
127 [ + + ]: 121 : if (dp->di_size < 0) {
128 : : #if GDBM_VERSION_MAJOR >= 1 && GDBM_VERSION_MINOR >= 11
129 : 85 : errno = 0;
130 : : gdbm_count_t count;
131 [ - + ]: 85 : if (gdbm_count(dp->di_dbm, &count) == -1) {
132 [ # # ]: 0 : if (errno != 0) {
133 : 0 : PyErr_SetFromErrno(state->gdbm_error);
134 : : }
135 : : else {
136 : 0 : PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
137 : : }
138 : 0 : return -1;
139 : : }
140 [ - + ]: 85 : if (count > PY_SSIZE_T_MAX) {
141 : 0 : PyErr_SetString(PyExc_OverflowError, "count exceeds PY_SSIZE_T_MAX");
142 : 0 : return -1;
143 : : }
144 : 85 : 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 : 121 : 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
169 : 734 : parse_datum(PyObject *o, datum *d, const char *failmsg)
170 : : {
171 : : Py_ssize_t size;
172 [ - + ]: 734 : if (!PyArg_Parse(o, "s#", &d->dptr, &size)) {
173 [ # # ]: 0 : if (failmsg != NULL) {
174 : 0 : PyErr_SetString(PyExc_TypeError, failmsg);
175 : : }
176 : 0 : return 0;
177 : : }
178 [ - + ]: 734 : if (INT_MAX < size) {
179 : 0 : PyErr_SetString(PyExc_OverflowError, "size does not fit in an int");
180 : 0 : return 0;
181 : : }
182 : 734 : d->dsize = size;
183 : 734 : return 1;
184 : : }
185 : :
186 : : static PyObject *
187 : 353 : gdbm_subscript(gdbmobject *dp, PyObject *key)
188 : : {
189 : : PyObject *v;
190 : : datum drec, krec;
191 : 353 : _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
192 : :
193 [ - + ]: 353 : if (!parse_datum(key, &krec, NULL)) {
194 : 0 : return NULL;
195 : : }
196 [ + + ]: 353 : if (dp->di_dbm == NULL) {
197 : 1 : PyErr_SetString(state->gdbm_error,
198 : : "GDBM object has already been closed");
199 : 1 : return NULL;
200 : : }
201 : 352 : drec = gdbm_fetch(dp->di_dbm, krec);
202 [ + + ]: 352 : if (drec.dptr == 0) {
203 : 57 : PyErr_SetObject(PyExc_KeyError, key);
204 : 57 : return NULL;
205 : : }
206 : 295 : v = PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
207 : 295 : free(drec.dptr);
208 : 295 : 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 *
222 : 7 : _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 : 7 : res = gdbm_subscript(self, key);
228 [ + + + - ]: 7 : if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
229 : 4 : PyErr_Clear();
230 : 4 : Py_INCREF(default_value);
231 : 4 : return default_value;
232 : : }
233 : 3 : return res;
234 : : }
235 : :
236 : : static int
237 : 213 : gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
238 : : {
239 : : datum krec, drec;
240 : 213 : const char *failmsg = "gdbm mappings have bytes or string indices only";
241 : 213 : _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
242 : :
243 [ - + ]: 213 : if (!parse_datum(v, &krec, failmsg)) {
244 : 0 : return -1;
245 : : }
246 [ - + ]: 213 : if (dp->di_dbm == NULL) {
247 : 0 : PyErr_SetString(state->gdbm_error,
248 : : "GDBM object has already been closed");
249 : 0 : return -1;
250 : : }
251 : 213 : dp->di_size = -1;
252 [ + + ]: 213 : if (w == NULL) {
253 [ + + ]: 45 : if (gdbm_delete(dp->di_dbm, krec) < 0) {
254 [ - + ]: 2 : if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
255 : 0 : PyErr_SetObject(PyExc_KeyError, v);
256 : : }
257 : : else {
258 : 2 : PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
259 : : }
260 : 2 : return -1;
261 : : }
262 : : }
263 : : else {
264 [ - + ]: 168 : if (!parse_datum(w, &drec, failmsg)) {
265 : 0 : return -1;
266 : : }
267 : 168 : errno = 0;
268 [ + + ]: 168 : if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
269 [ - + ]: 1 : if (errno != 0)
270 : 0 : PyErr_SetFromErrno(state->gdbm_error);
271 : : else
272 : 1 : PyErr_SetString(state->gdbm_error,
273 : 1 : gdbm_strerror(gdbm_errno));
274 : 1 : return -1;
275 : : }
276 : : }
277 : 210 : 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 *
291 : 3 : _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 : 3 : res = gdbm_subscript(self, key);
298 [ + + + - ]: 3 : if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
299 : 2 : PyErr_Clear();
300 [ - + ]: 2 : if (gdbm_ass_sub(self, key, default_value) < 0)
301 : 0 : return NULL;
302 : 2 : return gdbm_subscript(self, key);
303 : : }
304 : 1 : 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 *
314 : 215 : _gdbm_gdbm_close_impl(gdbmobject *self)
315 : : /*[clinic end generated code: output=f5abb4d6bb9e52d5 input=0a203447379b45fd]*/
316 : : {
317 [ + + ]: 215 : if (self->di_dbm) {
318 : 213 : gdbm_close(self->di_dbm);
319 : : }
320 : 215 : self->di_dbm = NULL;
321 : 215 : 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 *
334 : 250 : _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 : 250 : _gdbm_state *state = PyType_GetModuleState(cls);
342 : : assert(state != NULL);
343 : :
344 [ + - - + ]: 250 : if (self == NULL || !Py_IS_TYPE(self, state->gdbm_type)) {
345 : 0 : PyErr_BadInternalCall();
346 : 0 : return NULL;
347 : : }
348 [ + + ]: 250 : check_gdbmobject_open(self, state->gdbm_error);
349 : :
350 : 249 : v = PyList_New(0);
351 [ - + ]: 249 : if (v == NULL)
352 : 0 : return NULL;
353 : :
354 : 249 : key = gdbm_firstkey(self->di_dbm);
355 [ + + ]: 535 : while (key.dptr) {
356 : 286 : item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
357 [ - + ]: 286 : if (item == NULL) {
358 : 0 : free(key.dptr);
359 : 0 : Py_DECREF(v);
360 : 0 : return NULL;
361 : : }
362 : 286 : err = PyList_Append(v, item);
363 : 286 : Py_DECREF(item);
364 [ - + ]: 286 : if (err != 0) {
365 : 0 : free(key.dptr);
366 : 0 : Py_DECREF(v);
367 : 0 : return NULL;
368 : : }
369 : 286 : nextkey = gdbm_nextkey(self->di_dbm, key);
370 : 286 : free(key.dptr);
371 : 286 : key = nextkey;
372 : : }
373 : 249 : return v;
374 : : }
375 : :
376 : : static int
377 : 78 : gdbm_contains(PyObject *self, PyObject *arg)
378 : : {
379 : 78 : gdbmobject *dp = (gdbmobject *)self;
380 : : datum key;
381 : : Py_ssize_t size;
382 : 78 : _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
383 : :
384 [ - + ]: 78 : if ((dp)->di_dbm == NULL) {
385 : 0 : PyErr_SetString(state->gdbm_error,
386 : : "GDBM object has already been closed");
387 : 0 : return -1;
388 : : }
389 [ + + ]: 78 : if (PyUnicode_Check(arg)) {
390 : 2 : key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
391 : 2 : key.dsize = size;
392 [ - + ]: 2 : if (key.dptr == NULL)
393 : 0 : return -1;
394 : : }
395 [ - + ]: 76 : else if (!PyBytes_Check(arg)) {
396 : 0 : PyErr_Format(PyExc_TypeError,
397 : : "gdbm key must be bytes or string, not %.100s",
398 : 0 : Py_TYPE(arg)->tp_name);
399 : 0 : return -1;
400 : : }
401 : : else {
402 : 76 : key.dptr = PyBytes_AS_STRING(arg);
403 : 76 : key.dsize = PyBytes_GET_SIZE(arg);
404 : : }
405 : 78 : 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 *
421 : 1 : _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 : 1 : _gdbm_state *state = PyType_GetModuleState(cls);
427 : : assert(state != NULL);
428 : :
429 [ - + ]: 1 : check_gdbmobject_open(self, state->gdbm_error);
430 : 1 : key = gdbm_firstkey(self->di_dbm);
431 [ + - ]: 1 : if (key.dptr) {
432 : 1 : v = PyBytes_FromStringAndSize(key.dptr, key.dsize);
433 : 1 : free(key.dptr);
434 : 1 : return v;
435 : : }
436 : : else {
437 : 0 : 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 *
460 : 3 : _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 : 3 : _gdbm_state *state = PyType_GetModuleState(cls);
467 : : assert(state != NULL);
468 : :
469 : 3 : dbm_key.dptr = (char *)key;
470 : 3 : dbm_key.dsize = key_length;
471 [ - + ]: 3 : check_gdbmobject_open(self, state->gdbm_error);
472 : 3 : nextkey = gdbm_nextkey(self->di_dbm, dbm_key);
473 [ + + ]: 3 : if (nextkey.dptr) {
474 : 2 : v = PyBytes_FromStringAndSize(nextkey.dptr, nextkey.dsize);
475 : 2 : free(nextkey.dptr);
476 : 2 : return v;
477 : : }
478 : : else {
479 : 1 : 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 *
498 : 1 : _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls)
499 : : /*[clinic end generated code: output=d77c69e8e3dd644a input=e1359faeef844e46]*/
500 : : {
501 : 1 : _gdbm_state *state = PyType_GetModuleState(cls);
502 : : assert(state != NULL);
503 [ - + ]: 1 : check_gdbmobject_open(self, state->gdbm_error);
504 : 1 : errno = 0;
505 [ - + ]: 1 : if (gdbm_reorganize(self->di_dbm) < 0) {
506 [ # # ]: 0 : if (errno != 0)
507 : 0 : PyErr_SetFromErrno(state->gdbm_error);
508 : : else
509 : 0 : PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
510 : 0 : return NULL;
511 : : }
512 : 1 : 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 *
527 : 163 : _gdbm_gdbm_sync_impl(gdbmobject *self, PyTypeObject *cls)
528 : : /*[clinic end generated code: output=bb680a2035c3f592 input=3d749235f79b6f2a]*/
529 : : {
530 : 163 : _gdbm_state *state = PyType_GetModuleState(cls);
531 : : assert(state != NULL);
532 [ - + ]: 163 : check_gdbmobject_open(self, state->gdbm_error);
533 : 163 : gdbm_sync(self->di_dbm);
534 : 163 : Py_RETURN_NONE;
535 : : }
536 : :
537 : : static PyObject *
538 : 14 : gdbm__enter__(PyObject *self, PyObject *args)
539 : : {
540 : 14 : Py_INCREF(self);
541 : 14 : return self;
542 : : }
543 : :
544 : : static PyObject *
545 : 14 : gdbm__exit__(PyObject *self, PyObject *args)
546 : : {
547 : 14 : 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 *
623 : 216 : 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 : 216 : _gdbm_state *state = get_gdbm_state(module);
629 : : assert(state != NULL);
630 : :
631 [ + + + + : 216 : switch (flags[0]) {
- ]
632 : 15 : case 'r':
633 : 15 : iflags = GDBM_READER;
634 : 15 : break;
635 : 5 : case 'w':
636 : 5 : iflags = GDBM_WRITER;
637 : 5 : break;
638 : 187 : case 'c':
639 : 187 : iflags = GDBM_WRCREAT;
640 : 187 : break;
641 : 9 : case 'n':
642 : 9 : iflags = GDBM_NEWDB;
643 : 9 : break;
644 : 0 : default:
645 : 0 : PyErr_SetString(state->gdbm_error,
646 : : "First flag must be one of 'r', 'w', 'c' or 'n'");
647 : 0 : return NULL;
648 : : }
649 [ + + ]: 228 : for (flags++; *flags != '\0'; flags++) {
650 : : char buf[40];
651 [ + + + + ]: 13 : switch (*flags) {
652 : : #ifdef GDBM_FAST
653 : 4 : case 'f':
654 : 4 : iflags |= GDBM_FAST;
655 : 4 : break;
656 : : #endif
657 : : #ifdef GDBM_SYNC
658 : 4 : case 's':
659 : 4 : iflags |= GDBM_SYNC;
660 : 4 : break;
661 : : #endif
662 : : #ifdef GDBM_NOLOCK
663 : 4 : case 'u':
664 : 4 : iflags |= GDBM_NOLOCK;
665 : 4 : break;
666 : : #endif
667 : 1 : default:
668 : 1 : PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.",
669 : 1 : *flags);
670 : 1 : PyErr_SetString(state->gdbm_error, buf);
671 : 1 : return NULL;
672 : : }
673 : : }
674 : :
675 : : PyObject *filenamebytes;
676 [ - + ]: 215 : if (!PyUnicode_FSConverter(filename, &filenamebytes)) {
677 : 0 : return NULL;
678 : : }
679 : :
680 : 215 : const char *name = PyBytes_AS_STRING(filenamebytes);
681 [ - + ]: 215 : if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
682 : 0 : Py_DECREF(filenamebytes);
683 : 0 : PyErr_SetString(PyExc_ValueError, "embedded null character");
684 : 0 : return NULL;
685 : : }
686 : 215 : PyObject *self = newgdbmobject(state, name, iflags, mode);
687 : 215 : Py_DECREF(filenamebytes);
688 : 215 : 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
709 : 6 : _gdbm_exec(PyObject *module)
710 : : {
711 : 6 : _gdbm_state *state = get_gdbm_state(module);
712 : 6 : state->gdbm_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
713 : : &gdbmtype_spec, NULL);
714 [ - + ]: 6 : if (state->gdbm_type == NULL) {
715 : 0 : return -1;
716 : : }
717 : 6 : state->gdbm_error = PyErr_NewException("_gdbm.error", PyExc_OSError, NULL);
718 [ - + ]: 6 : if (state->gdbm_error == NULL) {
719 : 0 : return -1;
720 : : }
721 [ - + ]: 6 : if (PyModule_AddType(module, (PyTypeObject *)state->gdbm_error) < 0) {
722 : 0 : return -1;
723 : : }
724 [ - + ]: 6 : if (PyModule_AddStringConstant(module, "open_flags",
725 : : gdbmmodule_open_flags) < 0) {
726 : 0 : return -1;
727 : : }
728 : :
729 : : #if defined(GDBM_VERSION_MAJOR) && defined(GDBM_VERSION_MINOR) && \
730 : : defined(GDBM_VERSION_PATCH)
731 : 6 : PyObject *obj = Py_BuildValue("iii", GDBM_VERSION_MAJOR,
732 : : GDBM_VERSION_MINOR, GDBM_VERSION_PATCH);
733 [ - + ]: 6 : if (obj == NULL) {
734 : 0 : return -1;
735 : : }
736 [ - + ]: 6 : if (PyModule_AddObject(module, "_GDBM_VERSION", obj) < 0) {
737 : 0 : Py_DECREF(obj);
738 : 0 : return -1;
739 : : }
740 : : #endif
741 : 6 : return 0;
742 : : }
743 : :
744 : : static int
745 : 146 : _gdbm_module_traverse(PyObject *module, visitproc visit, void *arg)
746 : : {
747 : 146 : _gdbm_state *state = get_gdbm_state(module);
748 [ + - - + ]: 146 : Py_VISIT(state->gdbm_error);
749 [ + - - + ]: 146 : Py_VISIT(state->gdbm_type);
750 : 146 : return 0;
751 : : }
752 : :
753 : : static int
754 : 12 : _gdbm_module_clear(PyObject *module)
755 : : {
756 : 12 : _gdbm_state *state = get_gdbm_state(module);
757 [ + + ]: 12 : Py_CLEAR(state->gdbm_error);
758 [ + + ]: 12 : Py_CLEAR(state->gdbm_type);
759 : 12 : return 0;
760 : : }
761 : :
762 : : static void
763 : 6 : _gdbm_module_free(void *module)
764 : : {
765 : 6 : _gdbm_module_clear((PyObject *)module);
766 : 6 : }
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
786 : 6 : PyInit__gdbm(void)
787 : : {
788 : 6 : return PyModuleDef_Init(&_gdbmmodule);
789 : : }
|