1
2 /* DBM module using dictionary interface */
3
4
5 #define PY_SSIZE_T_CLEAN
6 #include "Python.h"
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11
12 /* Some Linux systems install gdbm/ndbm.h, but not ndbm.h. This supports
13 * whichever configure was able to locate.
14 */
15 #if defined(HAVE_NDBM_H)
16 #include <ndbm.h>
17 static const char which_dbm[] = "GNU gdbm"; /* EMX port of GDBM */
18 #elif defined(HAVE_GDBM_NDBM_H)
19 #include <gdbm/ndbm.h>
20 static const char which_dbm[] = "GNU gdbm";
21 #elif defined(HAVE_GDBM_DASH_NDBM_H)
22 #include <gdbm-ndbm.h>
23 static const char which_dbm[] = "GNU gdbm";
24 #elif defined(HAVE_BERKDB_H)
25 #include <db.h>
26 static const char which_dbm[] = "Berkeley DB";
27 #else
28 #error "No ndbm.h available!"
29 #endif
30
31 /*[clinic input]
32 module _dbm
33 class _dbm.dbm "dbmobject *" "&Dbmtype"
34 [clinic start generated code]*/
35 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b1aa8756d16150e]*/
36
37 typedef struct {
38 PyObject_HEAD
39 int flags;
40 int di_size; /* -1 means recompute */
41 DBM *di_dbm;
42 } dbmobject;
43
44 #include "clinic/_dbmmodule.c.h"
45
46 static PyTypeObject Dbmtype;
47
48 #define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype)
49 #define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
50 { PyErr_SetString(DbmError, "DBM object has already been closed"); \
51 return NULL; }
52
53 static PyObject *DbmError;
54
55 static PyObject *
newdbmobject(const char * file,int flags,int mode)56 newdbmobject(const char *file, int flags, int mode)
57 {
58 dbmobject *dp;
59
60 dp = PyObject_New(dbmobject, &Dbmtype);
61 if (dp == NULL)
62 return NULL;
63 dp->di_size = -1;
64 dp->flags = flags;
65 /* See issue #19296 */
66 if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) {
67 PyErr_SetFromErrnoWithFilename(DbmError, file);
68 Py_DECREF(dp);
69 return NULL;
70 }
71 return (PyObject *)dp;
72 }
73
74 /* Methods */
75
76 static void
dbm_dealloc(dbmobject * dp)77 dbm_dealloc(dbmobject *dp)
78 {
79 if ( dp->di_dbm )
80 dbm_close(dp->di_dbm);
81 PyObject_Del(dp);
82 }
83
84 static Py_ssize_t
dbm_length(dbmobject * dp)85 dbm_length(dbmobject *dp)
86 {
87 if (dp->di_dbm == NULL) {
88 PyErr_SetString(DbmError, "DBM object has already been closed");
89 return -1;
90 }
91 if ( dp->di_size < 0 ) {
92 datum key;
93 int size;
94
95 size = 0;
96 for ( key=dbm_firstkey(dp->di_dbm); key.dptr;
97 key = dbm_nextkey(dp->di_dbm))
98 size++;
99 dp->di_size = size;
100 }
101 return dp->di_size;
102 }
103
104 static PyObject *
dbm_subscript(dbmobject * dp,PyObject * key)105 dbm_subscript(dbmobject *dp, PyObject *key)
106 {
107 datum drec, krec;
108 Py_ssize_t tmp_size;
109
110 if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) )
111 return NULL;
112
113 krec.dsize = tmp_size;
114 check_dbmobject_open(dp);
115 drec = dbm_fetch(dp->di_dbm, krec);
116 if ( drec.dptr == 0 ) {
117 PyErr_SetObject(PyExc_KeyError, key);
118 return NULL;
119 }
120 if ( dbm_error(dp->di_dbm) ) {
121 dbm_clearerr(dp->di_dbm);
122 PyErr_SetString(DbmError, "");
123 return NULL;
124 }
125 return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
126 }
127
128 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)129 dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
130 {
131 datum krec, drec;
132 Py_ssize_t tmp_size;
133
134 if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
135 PyErr_SetString(PyExc_TypeError,
136 "dbm mappings have bytes or string keys only");
137 return -1;
138 }
139 krec.dsize = tmp_size;
140 if (dp->di_dbm == NULL) {
141 PyErr_SetString(DbmError, "DBM object has already been closed");
142 return -1;
143 }
144 dp->di_size = -1;
145 if (w == NULL) {
146 if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
147 dbm_clearerr(dp->di_dbm);
148 /* we might get a failure for reasons like file corrupted,
149 but we are not able to distinguish it */
150 if (dp->flags & O_RDWR) {
151 PyErr_SetObject(PyExc_KeyError, v);
152 }
153 else {
154 PyErr_SetString(DbmError, "cannot delete item from database");
155 }
156 return -1;
157 }
158 } else {
159 if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
160 PyErr_SetString(PyExc_TypeError,
161 "dbm mappings have bytes or string elements only");
162 return -1;
163 }
164 drec.dsize = tmp_size;
165 if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) {
166 dbm_clearerr(dp->di_dbm);
167 PyErr_SetString(DbmError,
168 "cannot add item to database");
169 return -1;
170 }
171 }
172 if ( dbm_error(dp->di_dbm) ) {
173 dbm_clearerr(dp->di_dbm);
174 PyErr_SetString(DbmError, "");
175 return -1;
176 }
177 return 0;
178 }
179
180 static PyMappingMethods dbm_as_mapping = {
181 (lenfunc)dbm_length, /*mp_length*/
182 (binaryfunc)dbm_subscript, /*mp_subscript*/
183 (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/
184 };
185
186 /*[clinic input]
187 _dbm.dbm.close
188
189 Close the database.
190 [clinic start generated code]*/
191
192 static PyObject *
_dbm_dbm_close_impl(dbmobject * self)193 _dbm_dbm_close_impl(dbmobject *self)
194 /*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/
195 {
196 if (self->di_dbm)
197 dbm_close(self->di_dbm);
198 self->di_dbm = NULL;
199 Py_RETURN_NONE;
200 }
201
202 /*[clinic input]
203 _dbm.dbm.keys
204
205 Return a list of all keys in the database.
206 [clinic start generated code]*/
207
208 static PyObject *
_dbm_dbm_keys_impl(dbmobject * self)209 _dbm_dbm_keys_impl(dbmobject *self)
210 /*[clinic end generated code: output=434549f7c121b33c input=d210ba778cd9c68a]*/
211 {
212 PyObject *v, *item;
213 datum key;
214 int err;
215
216 check_dbmobject_open(self);
217 v = PyList_New(0);
218 if (v == NULL)
219 return NULL;
220 for (key = dbm_firstkey(self->di_dbm); key.dptr;
221 key = dbm_nextkey(self->di_dbm)) {
222 item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
223 if (item == NULL) {
224 Py_DECREF(v);
225 return NULL;
226 }
227 err = PyList_Append(v, item);
228 Py_DECREF(item);
229 if (err != 0) {
230 Py_DECREF(v);
231 return NULL;
232 }
233 }
234 return v;
235 }
236
237 static int
dbm_contains(PyObject * self,PyObject * arg)238 dbm_contains(PyObject *self, PyObject *arg)
239 {
240 dbmobject *dp = (dbmobject *)self;
241 datum key, val;
242 Py_ssize_t size;
243
244 if ((dp)->di_dbm == NULL) {
245 PyErr_SetString(DbmError,
246 "DBM object has already been closed");
247 return -1;
248 }
249 if (PyUnicode_Check(arg)) {
250 key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
251 key.dsize = size;
252 if (key.dptr == NULL)
253 return -1;
254 }
255 else if (!PyBytes_Check(arg)) {
256 PyErr_Format(PyExc_TypeError,
257 "dbm key must be bytes or string, not %.100s",
258 arg->ob_type->tp_name);
259 return -1;
260 }
261 else {
262 key.dptr = PyBytes_AS_STRING(arg);
263 key.dsize = PyBytes_GET_SIZE(arg);
264 }
265 val = dbm_fetch(dp->di_dbm, key);
266 return val.dptr != NULL;
267 }
268
269 static PySequenceMethods dbm_as_sequence = {
270 0, /* sq_length */
271 0, /* sq_concat */
272 0, /* sq_repeat */
273 0, /* sq_item */
274 0, /* sq_slice */
275 0, /* sq_ass_item */
276 0, /* sq_ass_slice */
277 dbm_contains, /* sq_contains */
278 0, /* sq_inplace_concat */
279 0, /* sq_inplace_repeat */
280 };
281
282 /*[clinic input]
283 _dbm.dbm.get
284
285 key: str(accept={str, robuffer}, zeroes=True)
286 default: object = None
287 /
288
289 Return the value for key if present, otherwise default.
290 [clinic start generated code]*/
291
292 static PyObject *
_dbm_dbm_get_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length,PyObject * default_value)293 _dbm_dbm_get_impl(dbmobject *self, const char *key,
294 Py_ssize_clean_t key_length, PyObject *default_value)
295 /*[clinic end generated code: output=b44f95eba8203d93 input=b788eba0ffad2e91]*/
296 /*[clinic end generated code: output=4f5c0e523eaf1251 input=9402c0af8582dc69]*/
297 {
298 datum dbm_key, val;
299
300 dbm_key.dptr = (char *)key;
301 dbm_key.dsize = key_length;
302 check_dbmobject_open(self);
303 val = dbm_fetch(self->di_dbm, dbm_key);
304 if (val.dptr != NULL)
305 return PyBytes_FromStringAndSize(val.dptr, val.dsize);
306
307 Py_INCREF(default_value);
308 return default_value;
309 }
310
311 /*[clinic input]
312 _dbm.dbm.setdefault
313 key: str(accept={str, robuffer}, zeroes=True)
314 default: object(c_default="NULL") = b''
315 /
316
317 Return the value for key if present, otherwise default.
318
319 If key is not in the database, it is inserted with default as the value.
320 [clinic start generated code]*/
321
322 static PyObject *
_dbm_dbm_setdefault_impl(dbmobject * self,const char * key,Py_ssize_clean_t key_length,PyObject * default_value)323 _dbm_dbm_setdefault_impl(dbmobject *self, const char *key,
324 Py_ssize_clean_t key_length,
325 PyObject *default_value)
326 /*[clinic end generated code: output=52545886cf272161 input=bf40c48edaca01d6]*/
327 {
328 datum dbm_key, val;
329 Py_ssize_t tmp_size;
330
331 dbm_key.dptr = (char *)key;
332 dbm_key.dsize = key_length;
333 check_dbmobject_open(self);
334 val = dbm_fetch(self->di_dbm, dbm_key);
335 if (val.dptr != NULL)
336 return PyBytes_FromStringAndSize(val.dptr, val.dsize);
337 if (default_value == NULL) {
338 default_value = PyBytes_FromStringAndSize(NULL, 0);
339 if (default_value == NULL)
340 return NULL;
341 val.dptr = NULL;
342 val.dsize = 0;
343 }
344 else {
345 if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
346 PyErr_SetString(PyExc_TypeError,
347 "dbm mappings have bytes or string elements only");
348 return NULL;
349 }
350 val.dsize = tmp_size;
351 Py_INCREF(default_value);
352 }
353 if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) {
354 dbm_clearerr(self->di_dbm);
355 PyErr_SetString(DbmError, "cannot add item to database");
356 Py_DECREF(default_value);
357 return NULL;
358 }
359 return default_value;
360 }
361
362 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)363 dbm__enter__(PyObject *self, PyObject *args)
364 {
365 Py_INCREF(self);
366 return self;
367 }
368
369 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)370 dbm__exit__(PyObject *self, PyObject *args)
371 {
372 _Py_IDENTIFIER(close);
373 return _PyObject_CallMethodId(self, &PyId_close, NULL);
374 }
375
376
377 static PyMethodDef dbm_methods[] = {
378 _DBM_DBM_CLOSE_METHODDEF
379 _DBM_DBM_KEYS_METHODDEF
380 _DBM_DBM_GET_METHODDEF
381 _DBM_DBM_SETDEFAULT_METHODDEF
382 {"__enter__", dbm__enter__, METH_NOARGS, NULL},
383 {"__exit__", dbm__exit__, METH_VARARGS, NULL},
384 {NULL, NULL} /* sentinel */
385 };
386
387 static PyTypeObject Dbmtype = {
388 PyVarObject_HEAD_INIT(NULL, 0)
389 "_dbm.dbm",
390 sizeof(dbmobject),
391 0,
392 (destructor)dbm_dealloc, /*tp_dealloc*/
393 0, /*tp_vectorcall_offset*/
394 0, /*tp_getattr*/
395 0, /*tp_setattr*/
396 0, /*tp_as_async*/
397 0, /*tp_repr*/
398 0, /*tp_as_number*/
399 &dbm_as_sequence, /*tp_as_sequence*/
400 &dbm_as_mapping, /*tp_as_mapping*/
401 0, /*tp_hash*/
402 0, /*tp_call*/
403 0, /*tp_str*/
404 0, /*tp_getattro*/
405 0, /*tp_setattro*/
406 0, /*tp_as_buffer*/
407 Py_TPFLAGS_DEFAULT, /*tp_flags*/
408 0, /*tp_doc*/
409 0, /*tp_traverse*/
410 0, /*tp_clear*/
411 0, /*tp_richcompare*/
412 0, /*tp_weaklistoffset*/
413 0, /*tp_iter*/
414 0, /*tp_iternext*/
415 dbm_methods, /*tp_methods*/
416 };
417
418 /* ----------------------------------------------------------------- */
419
420 /*[clinic input]
421
422 _dbm.open as dbmopen
423
424 filename: unicode
425 The filename to open.
426
427 flags: str="r"
428 How to open the file. "r" for reading, "w" for writing, etc.
429
430 mode: int(py_default="0o666") = 0o666
431 If creating a new file, the mode bits for the new file
432 (e.g. os.O_RDWR).
433
434 /
435
436 Return a database object.
437
438 [clinic start generated code]*/
439
440 static PyObject *
dbmopen_impl(PyObject * module,PyObject * filename,const char * flags,int mode)441 dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
442 int mode)
443 /*[clinic end generated code: output=9527750f5df90764 input=376a9d903a50df59]*/
444 {
445 int iflags;
446
447 if ( strcmp(flags, "r") == 0 )
448 iflags = O_RDONLY;
449 else if ( strcmp(flags, "w") == 0 )
450 iflags = O_RDWR;
451 else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */
452 iflags = O_RDWR|O_CREAT;
453 else if ( strcmp(flags, "c") == 0 )
454 iflags = O_RDWR|O_CREAT;
455 else if ( strcmp(flags, "n") == 0 )
456 iflags = O_RDWR|O_CREAT|O_TRUNC;
457 else {
458 PyErr_SetString(DbmError,
459 "arg 2 to open should be 'r', 'w', 'c', or 'n'");
460 return NULL;
461 }
462
463 PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename);
464 if (filenamebytes == NULL) {
465 return NULL;
466 }
467 const char *name = PyBytes_AS_STRING(filenamebytes);
468 if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
469 Py_DECREF(filenamebytes);
470 PyErr_SetString(PyExc_ValueError, "embedded null character");
471 return NULL;
472 }
473 PyObject *self = newdbmobject(name, iflags, mode);
474 Py_DECREF(filenamebytes);
475 return self;
476 }
477
478 static PyMethodDef dbmmodule_methods[] = {
479 DBMOPEN_METHODDEF
480 { 0, 0 },
481 };
482
483
484 static struct PyModuleDef _dbmmodule = {
485 PyModuleDef_HEAD_INIT,
486 "_dbm",
487 NULL,
488 -1,
489 dbmmodule_methods,
490 NULL,
491 NULL,
492 NULL,
493 NULL
494 };
495
496 PyMODINIT_FUNC
PyInit__dbm(void)497 PyInit__dbm(void) {
498 PyObject *m, *d, *s;
499
500 if (PyType_Ready(&Dbmtype) < 0)
501 return NULL;
502 m = PyModule_Create(&_dbmmodule);
503 if (m == NULL)
504 return NULL;
505 d = PyModule_GetDict(m);
506 if (DbmError == NULL)
507 DbmError = PyErr_NewException("_dbm.error",
508 PyExc_OSError, NULL);
509 s = PyUnicode_FromString(which_dbm);
510 if (s != NULL) {
511 PyDict_SetItemString(d, "library", s);
512 Py_DECREF(s);
513 }
514 if (DbmError != NULL)
515 PyDict_SetItemString(d, "error", DbmError);
516 if (PyErr_Occurred()) {
517 Py_DECREF(m);
518 m = NULL;
519 }
520 return m;
521 }
522