dbmmodule.c 9.38 KB
Newer Older
1 2 3 4

/* DBM module using dictionary interface */


5
#include "Python.h"
6 7 8 9

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
10 11 12 13 14

/* Some Linux systems install gdbm/ndbm.h, but not ndbm.h.  This supports
 * whichever configure was able to locate.
 */
#if defined(HAVE_NDBM_H)
15
#include <ndbm.h>
16
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
17
static char *which_dbm = "ndbm";
18 19 20
#else
static char *which_dbm = "GNU gdbm";  /* EMX port of GDBM */
#endif
21 22
#elif defined(HAVE_GDBM_NDBM_H)
#include <gdbm/ndbm.h>
23
static char *which_dbm = "GNU gdbm";
24 25 26
#elif defined(HAVE_BERKDB_H)
#include <db.h>
static char *which_dbm = "Berkeley DB";
27 28 29
#else
#error "No ndbm.h available!"
#endif
30 31

typedef struct {
32
	PyObject_HEAD
33 34 35 36
	int di_size;	/* -1 means recompute */
	DBM *di_dbm;
} dbmobject;

37
static PyTypeObject Dbmtype;
38

39
#define is_dbmobject(v) (Py_Type(v) == &Dbmtype)
40 41 42
#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \
               { PyErr_SetString(DbmError, "DBM object has already been closed"); \
                 return NULL; }
43

44
static PyObject *DbmError;
45

46
static PyObject *
47
newdbmobject(char *file, int flags, int mode)
48 49 50
{
        dbmobject *dp;

51
	dp = PyObject_New(dbmobject, &Dbmtype);
52 53 54 55
	if (dp == NULL)
		return NULL;
	dp->di_size = -1;
	if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) {
56 57 58
		PyErr_SetFromErrno(DbmError);
		Py_DECREF(dp);
		return NULL;
59
	}
60
	return (PyObject *)dp;
61 62 63 64 65
}

/* Methods */

static void
66
dbm_dealloc(register dbmobject *dp)
67 68
{
        if ( dp->di_dbm )
69
		dbm_close(dp->di_dbm);
70
	PyObject_Del(dp);
71 72
}

Martin v. Löwis's avatar
Martin v. Löwis committed
73
static Py_ssize_t
74
dbm_length(dbmobject *dp)
75
{
76 77 78 79
        if (dp->di_dbm == NULL) {
                 PyErr_SetString(DbmError, "DBM object has already been closed"); 
                 return -1; 
        }
80
        if ( dp->di_size < 0 ) {
81 82 83 84 85 86 87 88
		datum key;
		int size;

		size = 0;
		for ( key=dbm_firstkey(dp->di_dbm); key.dptr;
		      key = dbm_nextkey(dp->di_dbm))
			size++;
		dp->di_size = size;
89 90 91 92
	}
	return dp->di_size;
}

93
static PyObject *
94
dbm_subscript(dbmobject *dp, register PyObject *key)
95 96
{
	datum drec, krec;
97
	Py_ssize_t tmp_size;
98
	
99
	if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) )
100 101
		return NULL;
	
102
	krec.dsize = tmp_size;
103
        check_dbmobject_open(dp);
104 105
	drec = dbm_fetch(dp->di_dbm, krec);
	if ( drec.dptr == 0 ) {
106
		PyErr_SetObject(PyExc_KeyError, key);
107
		return NULL;
108 109
	}
	if ( dbm_error(dp->di_dbm) ) {
110 111 112
		dbm_clearerr(dp->di_dbm);
		PyErr_SetString(DbmError, "");
		return NULL;
113
	}
114
	return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
115 116 117
}

static int
118
dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
119 120
{
        datum krec, drec;
121
	Py_ssize_t tmp_size;
122
	
123
        if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
124
		PyErr_SetString(PyExc_TypeError,
125
				"dbm mappings have string keys only");
126
		return -1;
127
	}
128
	krec.dsize = tmp_size;
129 130 131 132
        if (dp->di_dbm == NULL) {
                 PyErr_SetString(DbmError, "DBM object has already been closed"); 
                 return -1;
        }
133 134
	dp->di_size = -1;
	if (w == NULL) {
135 136
		if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
			dbm_clearerr(dp->di_dbm);
137
			PyErr_SetObject(PyExc_KeyError, v);
138 139
			return -1;
		}
140
	} else {
141
		if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
142
			PyErr_SetString(PyExc_TypeError,
143
			     "dbm mappings have byte string elements only");
144 145
			return -1;
		}
146
		drec.dsize = tmp_size;
147 148
		if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) {
			dbm_clearerr(dp->di_dbm);
149
			PyErr_SetString(DbmError,
Fred Drake's avatar
Fred Drake committed
150
					"cannot add item to database");
151 152
			return -1;
		}
153 154
	}
	if ( dbm_error(dp->di_dbm) ) {
155 156 157
		dbm_clearerr(dp->di_dbm);
		PyErr_SetString(DbmError, "");
		return -1;
158 159 160 161
	}
	return 0;
}

162
static PyMappingMethods dbm_as_mapping = {
Martin v. Löwis's avatar
Martin v. Löwis committed
163
	(lenfunc)dbm_length,		/*mp_length*/
164 165
	(binaryfunc)dbm_subscript,	/*mp_subscript*/
	(objobjargproc)dbm_ass_sub,	/*mp_ass_subscript*/
166 167
};

168
static PyObject *
169
dbm__close(register dbmobject *dp, PyObject *unused)
170
{
171
        if (dp->di_dbm)
172
		dbm_close(dp->di_dbm);
173
	dp->di_dbm = NULL;
174 175
	Py_INCREF(Py_None);
	return Py_None;
176 177
}

178
static PyObject *
179
dbm_keys(register dbmobject *dp, PyObject *unused)
180
{
181
	register PyObject *v, *item;
182
	datum key;
183
	int err;
184

185
        check_dbmobject_open(dp);
186
	v = PyList_New(0);
187 188 189
	if (v == NULL)
		return NULL;
	for (key = dbm_firstkey(dp->di_dbm); key.dptr;
190
	     key = dbm_nextkey(dp->di_dbm)) {
191
		item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
192
		if (item == NULL) {
193
			Py_DECREF(v);
194 195
			return NULL;
		}
196 197
		err = PyList_Append(v, item);
		Py_DECREF(item);
198
		if (err != 0) {
199
			Py_DECREF(v);
200 201
			return NULL;
		}
202 203 204 205
	}
	return v;
}

206 207
static int
dbm_contains(PyObject *self, PyObject *arg)
208
{
209
	dbmobject *dp = (dbmobject *)self;
210
	datum key, val;
211 212 213 214 215 216

	if ((dp)->di_dbm == NULL) {
		PyErr_SetString(DbmError,
				"DBM object has already been closed");
                 return -1;
	}
217 218 219 220 221
	if (PyUnicode_Check(arg)) {
		arg = _PyUnicode_AsDefaultEncodedString(arg, NULL);
		if (arg == NULL)
			return -1;
	}
222
	if (!PyBytes_Check(arg)) {
223 224 225 226 227
		PyErr_Format(PyExc_TypeError,
			     "dbm key must be string, not %.100s",
			     arg->ob_type->tp_name);
		return -1;
	}
228 229
	key.dptr = PyBytes_AS_STRING(arg);
	key.dsize = PyBytes_GET_SIZE(arg);
230
	val = dbm_fetch(dp->di_dbm, key);
231
	return val.dptr != NULL;
232 233
}

234 235 236 237 238 239 240 241 242 243 244 245 246
static PySequenceMethods dbm_as_sequence = {
	0,			/* sq_length */
	0,			/* sq_concat */
	0,			/* sq_repeat */
	0,			/* sq_item */
	0,			/* sq_slice */
	0,			/* sq_ass_item */
	0,			/* sq_ass_slice */
	dbm_contains,		/* sq_contains */
	0,			/* sq_inplace_concat */
	0,			/* sq_inplace_repeat */
};

247 248 249 250 251
static PyObject *
dbm_get(register dbmobject *dp, PyObject *args)
{
	datum key, val;
	PyObject *defvalue = Py_None;
252
	char *tmp_ptr;
253
	Py_ssize_t tmp_size;
254 255

	if (!PyArg_ParseTuple(args, "s#|O:get",
256
                              &tmp_ptr, &tmp_size, &defvalue))
257
		return NULL;
258
	key.dptr = tmp_ptr;
259
	key.dsize = tmp_size;
260 261 262
        check_dbmobject_open(dp);
	val = dbm_fetch(dp->di_dbm, key);
	if (val.dptr != NULL)
263
		return PyBytes_FromStringAndSize(val.dptr, val.dsize);
264 265 266 267 268 269 270 271 272 273 274
	else {
		Py_INCREF(defvalue);
		return defvalue;
	}
}

static PyObject *
dbm_setdefault(register dbmobject *dp, PyObject *args)
{
	datum key, val;
	PyObject *defvalue = NULL;
275
	char *tmp_ptr;
276
	Py_ssize_t tmp_size;
277

278
	if (!PyArg_ParseTuple(args, "s#|O:setdefault",
279
                              &tmp_ptr, &tmp_size, &defvalue))
280
		return NULL;
281
	key.dptr = tmp_ptr;
282
	key.dsize = tmp_size;
283 284 285
        check_dbmobject_open(dp);
	val = dbm_fetch(dp->di_dbm, key);
	if (val.dptr != NULL)
286
		return PyBytes_FromStringAndSize(val.dptr, val.dsize);
287
	if (defvalue == NULL) {
288
		defvalue = PyBytes_FromStringAndSize(NULL, 0);
289 290
		if (defvalue == NULL)
			return NULL;
291 292
		val.dptr = NULL;
		val.dsize = 0;
293
	}
294 295 296 297 298 299 300
	else {
		if ( !PyArg_Parse(defvalue, "s#", &val.dptr, &tmp_size) ) {
			PyErr_SetString(PyExc_TypeError,
				"dbm mappings have byte string elements only");
			return NULL;
		}
		val.dsize = tmp_size;
301
		Py_INCREF(defvalue);
302
	}
303 304
	if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) {
		dbm_clearerr(dp->di_dbm);
Fred Drake's avatar
Fred Drake committed
305
		PyErr_SetString(DbmError, "cannot add item to database");
306
		Py_DECREF(defvalue);
307 308 309 310 311
		return NULL;
	}
	return defvalue;
}

312
static PyMethodDef dbm_methods[] = {
313
	{"close",	(PyCFunction)dbm__close,	METH_NOARGS,
314
	 "close()\nClose the database."},
315
	{"keys",	(PyCFunction)dbm_keys,		METH_NOARGS,
316 317 318 319 320 321 322 323
	 "keys() -> list\nReturn a list of all keys in the database."},
	{"get",		(PyCFunction)dbm_get,		METH_VARARGS,
	 "get(key[, default]) -> value\n"
	 "Return the value for key if present, otherwise default."},
	{"setdefault",	(PyCFunction)dbm_setdefault,	METH_VARARGS,
	 "setdefault(key[, default]) -> value\n"
	 "Return the value for key if present, otherwise default.  If key\n"
	 "is not in the database, it is inserted with default as the value."},
324 325 326
	{NULL,		NULL}		/* sentinel */
};

327
static PyObject *
328
dbm_getattr(dbmobject *dp, char *name)
329
{
330
	return Py_FindMethod(dbm_methods, (PyObject *)dp, name);
331 332
}

333
static PyTypeObject Dbmtype = {
334
	PyVarObject_HEAD_INIT(NULL, 0)
335
	"dbm.dbm",
336 337
	sizeof(dbmobject),
	0,
338 339
	(destructor)dbm_dealloc,  /*tp_dealloc*/
	0,			  /*tp_print*/
340
	(getattrfunc)dbm_getattr, /*tp_getattr*/
341 342 343 344
	0,			  /*tp_setattr*/
	0,			  /*tp_compare*/
	0,			  /*tp_repr*/
	0,			  /*tp_as_number*/
345
	&dbm_as_sequence,	  /*tp_as_sequence*/
346
	&dbm_as_mapping,	  /*tp_as_mapping*/
347 348 349 350
};

/* ----------------------------------------------------------------- */

351
static PyObject *
352
dbmopen(PyObject *self, PyObject *args)
353
{
354 355 356 357
	char *name;
	char *flags = "r";
	int iflags;
	int mode = 0666;
358

359
        if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) )
360
		return NULL;
361
	if ( strcmp(flags, "r") == 0 )
362
		iflags = O_RDONLY;
363
	else if ( strcmp(flags, "w") == 0 )
364
		iflags = O_RDWR;
365
	else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */
366
		iflags = O_RDWR|O_CREAT; 
367
	else if ( strcmp(flags, "c") == 0 )
368
		iflags = O_RDWR|O_CREAT;
369
	else if ( strcmp(flags, "n") == 0 )
370
		iflags = O_RDWR|O_CREAT|O_TRUNC;
371
	else {
372
		PyErr_SetString(DbmError,
Fred Drake's avatar
Fred Drake committed
373
				"arg 2 to open should be 'r', 'w', 'c', or 'n'");
374
		return NULL;
375 376 377 378
	}
        return newdbmobject(name, iflags, mode);
}

379
static PyMethodDef dbmmodule_methods[] = {
380 381 382
	{ "open", (PyCFunction)dbmopen, METH_VARARGS,
	  "open(path[, flag[, mode]]) -> mapping\n"
	  "Return a database object."},
383
	{ 0, 0 },
384 385
};

386
PyMODINIT_FUNC
387
initdbm(void) {
388
	PyObject *m, *d, *s;
389

390 391
	if (PyType_Ready(&Dbmtype) < 0)
		return;
392
	m = Py_InitModule("dbm", dbmmodule_methods);
393 394
	if (m == NULL)
		return;
395
	d = PyModule_GetDict(m);
396 397
	if (DbmError == NULL)
		DbmError = PyErr_NewException("dbm.error", NULL, NULL);
398
	s = PyUnicode_FromString(which_dbm);
399 400 401 402
	if (s != NULL) {
		PyDict_SetItemString(d, "library", s);
		Py_DECREF(s);
	}
403 404
	if (DbmError != NULL)
		PyDict_SetItemString(d, "error", DbmError);
405
}