I found that the python package in Release 2.3 and CURRENT isn't building the bsddb, Berkeley Database, Module as the _bsddb.c file isn't compatible with db-4.3. Unfortunately this isn't obvious during the build process as it requires careful perusal of the python build output to see which, if any, Modules didn't build properly.
I tried fixing this myself, looking at the perl Berkeley module, but couldn't get it to work properly. I then tried by using the _bsddb.c module from the current python CVS. This built, but then failed when attempting to access a cache.db file created using the openpkg-tools index program (a patch file is attached). Bill -- INTERNET: [EMAIL PROTECTED] Bill Campbell; Celestial Software LLC UUCP: camco!bill PO Box 820; 6641 E. Mercer Way FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676 URL: http://www.celestial.com/ The pinnacle of open systems is: when moving from vendor to vendor, the design flaws stay the same.
diff -ur ../Python-2.4.orig/Modules/_bsddb.c ./Modules/_bsddb.c --- ../Python-2.4.orig/Modules/_bsddb.c 2004-09-03 18:36:59.000000000 -0700 +++ ./Modules/_bsddb.c 2005-03-05 09:54:59.117184545 -0800 @@ -97,8 +97,8 @@ #error "eek! DBVER can't handle minor versions > 9" #endif -#define PY_BSDDB_VERSION "4.2.9" -static char *rcs_id = "$Id: _bsddb.c,v 1.38 2004/09/04 01:36:59 greg Exp $"; +#define PY_BSDDB_VERSION "4.3.0" +static char *rcs_id = "$Id: _bsddb.c,v 1.40 2004/12/16 09:47:28 greg Exp $"; #ifdef WITH_THREAD @@ -176,13 +176,16 @@ static PyObject* DBInvalidArgError; /* EINVAL */ static PyObject* DBAccessError; /* EACCES */ static PyObject* DBNoSpaceError; /* ENOSPC */ -static PyObject* DBNoMemoryError; /* ENOMEM */ +static PyObject* DBNoMemoryError; /* DB_BUFFER_SMALL (ENOMEM when < 4.3) */ static PyObject* DBAgainError; /* EAGAIN */ static PyObject* DBBusyError; /* EBUSY */ static PyObject* DBFileExistsError; /* EEXIST */ static PyObject* DBNoSuchFileError; /* ENOENT */ static PyObject* DBPermissionsError; /* EPERM */ +#if (DBVER < 43) +#define DB_BUFFER_SMALL ENOMEM +#endif /* --------------------------------------------------------------------- */ @@ -462,13 +465,35 @@ return 1; } +/* a safe strcpy() without the zeroing behaviour and semantics of strncpy. */ +/* TODO: make this use the native libc strlcpy() when available (BSD) */ +unsigned int our_strlcpy(char* dest, const char* src, unsigned int n) +{ + unsigned int srclen, copylen; + + srclen = strlen(src); + if (n <= 0) + return srclen; + copylen = (srclen > n-1) ? n-1 : srclen; + /* populate dest[0] thru dest[copylen-1] */ + memcpy(dest, src, copylen); + /* guarantee null termination */ + dest[copylen] = 0; + + return srclen; +} /* Callback used to save away more information about errors from the DB * library. */ static char _db_errmsg[1024]; +#if (DBVER <= 42) static void _db_errorCallback(const char* prefix, char* msg) +#else +static void _db_errorCallback(const DB_ENV *db_env, + const char* prefix, const char* msg) +#endif { - strcpy(_db_errmsg, msg); + our_strlcpy(_db_errmsg, msg, sizeof(_db_errmsg)); } @@ -486,7 +511,7 @@ #if (DBVER < 41) case DB_INCOMPLETE: #if INCOMPLETE_IS_WARNING - strcpy(errTxt, db_strerror(err)); + our_strlcpy(errTxt, db_strerror(err), sizeof(errTxt)); if (_db_errmsg[0]) { strcat(errTxt, " -- "); strcat(errTxt, _db_errmsg); @@ -520,11 +545,15 @@ case DB_PAGE_NOTFOUND: errObj = DBPageNotFoundError; break; case DB_SECONDARY_BAD: errObj = DBSecondaryBadError; break; #endif + case DB_BUFFER_SMALL: errObj = DBNoMemoryError; break; +#if (DBVER >= 43) + /* ENOMEM and DB_BUFFER_SMALL were one and the same until 4.3 */ + case ENOMEM: errObj = PyExc_MemoryError; break; +#endif case EINVAL: errObj = DBInvalidArgError; break; case EACCES: errObj = DBAccessError; break; case ENOSPC: errObj = DBNoSpaceError; break; - case ENOMEM: errObj = DBNoMemoryError; break; case EAGAIN: errObj = DBAgainError; break; case EBUSY : errObj = DBBusyError; break; case EEXIST: errObj = DBFileExistsError; break; @@ -535,8 +564,7 @@ } if (errObj != NULL) { - /* FIXME this needs proper bounds checking on errTxt */ - strcpy(errTxt, db_strerror(err)); + our_strlcpy(errTxt, db_strerror(err), sizeof(errTxt)); if (_db_errmsg[0]) { strcat(errTxt, " -- "); strcat(errTxt, _db_errmsg); @@ -1420,6 +1448,7 @@ return retval; } +#if (DBVER >= 33) static PyObject* DB_pget(DBObject* self, PyObject* args, PyObject* kwargs) { @@ -1507,6 +1536,7 @@ RETURN_IF_ERR(); return retval; } +#endif /* Return size of entry */ @@ -1533,14 +1563,14 @@ } CLEAR_DBT(data); - /* We don't allocate any memory, forcing a ENOMEM error and thus - getting the record size. */ + /* We don't allocate any memory, forcing a DB_BUFFER_SMALL error and + thus getting the record size. */ data.flags = DB_DBT_USERMEM; data.ulen = 0; MYDB_BEGIN_ALLOW_THREADS; err = self->db->get(self->db, txn, &key, &data, flags); MYDB_END_ALLOW_THREADS; - if (err == ENOMEM) { + if (err == DB_BUFFER_SMALL) { retval = PyInt_FromLong((long)data.size); err = 0; } @@ -2133,19 +2163,35 @@ #endif static PyObject* -DB_stat(DBObject* self, PyObject* args) +DB_stat(DBObject* self, PyObject* args, PyObject* kwargs) { int err, flags = 0, type; void* sp; PyObject* d; +#if (DBVER >= 43) + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "txn", "flags", NULL }; +#else + char* kwnames[] = { "flags", NULL }; +#endif - - if (!PyArg_ParseTuple(args, "|i:stat", &flags)) +#if (DBVER >= 43) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iO:stat", kwnames, + &flags, &txnobj)) return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:stat", kwnames, &flags)) + return NULL; +#endif CHECK_DB_NOT_CLOSED(self); MYDB_BEGIN_ALLOW_THREADS; -#if (DBVER >= 33) +#if (DBVER >= 43) + err = self->db->stat(self->db, txn, &sp, flags); +#elif (DBVER >= 33) err = self->db->stat(self->db, &sp, flags); #else err = self->db->stat(self->db, &sp, NULL, flags); @@ -2407,7 +2453,9 @@ } MYDB_BEGIN_ALLOW_THREADS; -#if (DBVER >= 33) +#if (DBVER >= 43) + err = self->db->stat(self->db, /*txnid*/ NULL, &sp, flags); +#elif (DBVER >= 33) err = self->db->stat(self->db, &sp, flags); #else err = self->db->stat(self->db, &sp, NULL, flags); @@ -2525,7 +2573,7 @@ return NULL; } - /* This causes ENOMEM to be returned when the db has the key because + /* This causes DB_BUFFER_SMALL to be returned when the db has the key because it has a record but can't allocate a buffer for the data. This saves having to deal with data we won't be using. */ @@ -2536,7 +2584,7 @@ err = self->db->get(self->db, txn, &key, &data, 0); MYDB_END_ALLOW_THREADS; FREE_DBT(key); - return PyInt_FromLong((err == ENOMEM) || (err == 0)); + return PyInt_FromLong((err == DB_BUFFER_SMALL) || (err == 0)); } @@ -2872,6 +2920,7 @@ return retval; } +#if (DBVER >= 33) static PyObject* DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs) { @@ -2971,6 +3020,7 @@ } return retval; } +#endif static PyObject* @@ -3281,15 +3331,15 @@ CLEAR_DBT(key); CLEAR_DBT(data); - /* We don't allocate any memory, forcing a ENOMEM error and thus + /* We don't allocate any memory, forcing a DB_BUFFER_SMALL error and thus getting the record size. */ data.flags = DB_DBT_USERMEM; data.ulen = 0; MYDB_BEGIN_ALLOW_THREADS; err = self->dbc->c_get(self->dbc, &key, &data, flags); MYDB_END_ALLOW_THREADS; - if (err == ENOMEM || !err) { - /* ENOMEM means positive size, !err means zero length value */ + if (err == DB_BUFFER_SMALL || !err) { + /* DB_BUFFER_SMALL means positive size, !err means zero length value */ retval = PyInt_FromLong((long)data.size); err = 0; } @@ -4333,7 +4383,9 @@ {"delete", (PyCFunction)DB_delete, METH_VARARGS|METH_KEYWORDS}, {"fd", (PyCFunction)DB_fd, METH_VARARGS}, {"get", (PyCFunction)DB_get, METH_VARARGS|METH_KEYWORDS}, +#if (DBVER >= 33) {"pget", (PyCFunction)DB_pget, METH_VARARGS|METH_KEYWORDS}, +#endif {"get_both", (PyCFunction)DB_get_both, METH_VARARGS|METH_KEYWORDS}, {"get_byteswapped", (PyCFunction)DB_get_byteswapped,METH_VARARGS}, {"get_size", (PyCFunction)DB_get_size, METH_VARARGS|METH_KEYWORDS}, @@ -4364,7 +4416,7 @@ #if (DBVER >= 32) {"set_q_extentsize",(PyCFunction)DB_set_q_extentsize,METH_VARARGS}, #endif - {"stat", (PyCFunction)DB_stat, METH_VARARGS}, + {"stat", (PyCFunction)DB_stat, METH_VARARGS|METH_KEYWORDS}, {"sync", (PyCFunction)DB_sync, METH_VARARGS}, #if (DBVER >= 33) {"truncate", (PyCFunction)DB_truncate, METH_VARARGS|METH_KEYWORDS}, @@ -4393,7 +4445,9 @@ {"dup", (PyCFunction)DBC_dup, METH_VARARGS}, {"first", (PyCFunction)DBC_first, METH_VARARGS|METH_KEYWORDS}, {"get", (PyCFunction)DBC_get, METH_VARARGS|METH_KEYWORDS}, +#if (DBVER >= 33) {"pget", (PyCFunction)DBC_pget, METH_VARARGS|METH_KEYWORDS}, +#endif {"get_recno", (PyCFunction)DBC_get_recno, METH_VARARGS}, {"last", (PyCFunction)DBC_last, METH_VARARGS|METH_KEYWORDS}, {"next", (PyCFunction)DBC_next, METH_VARARGS|METH_KEYWORDS}, @@ -4903,7 +4957,9 @@ #if (DBVER >= 33) ADD_INT(d, DB_LSTAT_ABORTED); +#if (DBVER < 43) ADD_INT(d, DB_LSTAT_ERR); +#endif ADD_INT(d, DB_LSTAT_FREE); ADD_INT(d, DB_LSTAT_HELD); #if (DBVER == 33) @@ -5029,6 +5085,11 @@ ADD_INT(d, DB_CHKSUM); #endif +#if (DBVER >= 43) + ADD_INT(d, DB_LOG_INMEMORY); + ADD_INT(d, DB_BUFFER_SMALL); +#endif + #if (DBVER >= 41) ADD_INT(d, DB_ENCRYPT_AES); ADD_INT(d, DB_AUTO_COMMIT);