serious bug in metalbase fixed!

lance@fox.com
Thu, 8 Sep 94 12:58:11 PDT

Ken Manheimer found a very serious bug in the metalbase module.
(Thank you Ken!)

I have fixed this bug and have also converted the file over to using the
new naming scheme. Below you will find the new metalbasemodule.c file.
I would have liked to send out diffs, but that does not work well
when you change almost every line.. :)

If anyone else finds any bugs, please let me know!

/***********************************************************
Copyright 1994 by Lance Ellinghouse,
Cathedral City, California Republic, United States of America

All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation.

LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

******************************************************************/

/* Metaldb objects */

#include "allobjects.h"
#include <errno.h>
#include "modsupport.h" /* For PyArg_Parse() etc. */
#include "mbase.h"

#include "rename1.h"

extern char *sys_errlist[];

static PyObject *PyMetalbase_ExcError;

/* This is similar to structmember, but only supports the items
I need and is more flexible for what I need. */

typedef struct {
char *name;
ftype type;
int offset;
int length;
int readonly;
int fld_num;
} _PyMetalbaseRec_FieldListT;

static PyObject *
_PyMetalbaseRec_GetField(rel, addr, flist, name)
relation *rel;
char *addr;
_PyMetalbaseRec_FieldListT *flist;
char *name;
{
_PyMetalbaseRec_FieldListT *l;
char *o_addr = addr;

for (l = flist; l->name != NULL; l++) {
if (strcmp(l->name, name) == 0) {
PyObject *v;
addr += l->offset;
switch(l->type) {
case T_PHONE:
{
mb_phone mbp;
mbp = *(mb_phone *)addr;
v = PyTuple_New(4);
PyTuple_SetItem(v,0,PyInt_FromLong(mbp.area));
PyTuple_SetItem(v,1,PyInt_FromLong(mbp.prefix));
PyTuple_SetItem(v,2,PyInt_FromLong(mbp.number));
PyTuple_SetItem(v,3,PyInt_FromLong(mbp.ext));
}
break;
case T_CHAR:
v = PyString_FromStringAndSize((char*)addr, l->length);
if (strlen(PyString_AsString(v)) < (size_t)l->length)
_PyString_Resize(&v,strlen(PyString_AsString(v)));
break;
case T_SHORT:
v = PyInt_FromLong((long) *(short*)addr);
break;
case T_USHORT:
v = PyInt_FromLong((long) *(unsigned short*)addr);
break;
case T_TIME:
{
mb_time mbt = *(mb_time *)addr;
v = PyTuple_New(4);
PyTuple_SetItem(v,0,PyInt_FromLong(GetHours(mbt)));
PyTuple_SetItem(v,1,PyInt_FromLong(GetMinutes(mbt)));
PyTuple_SetItem(v,2,PyInt_FromLong(GetSeconds(mbt)));
PyTuple_SetItem(v,3,PyInt_FromLong(GetMicros(mbt)));
}
break;
case T_DATE:
{
mb_date mbd = *(mb_date *)addr;
v = PyTuple_New(3);
PyTuple_SetItem(v,0,PyInt_FromLong(GetYear(mbd)));
PyTuple_SetItem(v,1,PyInt_FromLong(GetMonth(mbd)));
PyTuple_SetItem(v,2,PyInt_FromLong(GetDay(mbd)));
}
break;
case T_LONG:
case T_SERIAL:
v = PyInt_FromLong(*(long*)addr);
break;
case T_ULONG:
v = PyInt_FromLong((long) *(unsigned long*)addr);
break;
case T_FLOAT:
v = PyFloat_FromDouble((double)*(float*)addr);
break;
case T_DOUBLE:
case T_MONEY:
v = PyFloat_FromDouble(*(double*)addr);
break;
case T_BYTE:
v = PyString_FromStringAndSize((char*)addr, l->length);
break;
case T_MCHAR:
case T_MBYTE:
{
int fld = fieldOpen(rel, o_addr, l->fld_num);
struct stat S;
unsigned long l_pos = lseek(fld,1,0);
int count = 0;

if (fstat(fld,&S) == -1) {
PyErr_SetString(PyExc_IOError,sys_errlist[errno]);
close(fld);
return NULL;
}
count = S.st_size;
if (count != 0)
count--;
v = PyString_FromStringAndSize(NULL,count);
while (read(fld,PyString_AsString(v),count+1)== -1 &&
errno == EINTR) {
count++;
if (count > 10) {
PyErr_SetString(PyExc_IOError,sys_errlist[errno]);
close(fld);
return NULL;
}
lseek(fld,0,l_pos);
}
close(fld);
}
break;
}
return v;
}
}
PyErr_SetString(PyExc_AttributeError, name);
return NULL;
}

static int
_PyMetalbaseRec_SetField(rel, addr, flist, name, v, override)
relation *rel;
char *addr;
_PyMetalbaseRec_FieldListT *flist;
char *name;
PyObject *v;
int override;
{
_PyMetalbaseRec_FieldListT *l;
char *o_addr = addr;

for (l=flist; l->name != NULL; l++) {
if (strcmp(l->name, name) == 0) {
if (l->readonly && !override) {
PyErr_SetString(PyExc_TypeError, "readonly attribute");
return -1;
}
addr += l->offset;
switch (l->type) {
case T_CHAR:
if (!PyString_Check(v)) {
PyErr_BadArgument();
return -1;
}
strncpy((char *)addr,PyString_AsString(v),l->length);
break;
case T_SHORT:
case T_USHORT:
if (!PyInt_Check(v)) {
PyErr_BadArgument();
return -1;
}
if (l->type == T_SHORT) {
*(short *)addr = (short)PyInt_AsLong(v);
} else {
*(unsigned short *)addr = (unsigned short)PyInt_AsLong(v);
}
break;
case T_TIME:
if (PyString_Check(v)) {
*(mb_time *)addr = scn_time(PyString_AsString(v));
} else if (PyTuple_Check(v)) {
SetHours( (*(mb_time *)addr), PyInt_AsLong(PyTuple_GetItem(v,0)));
SetMinutes( (*(mb_time *)addr), PyInt_AsLong(PyTuple_GetItem(v,0)));
SetSeconds( (*(mb_time *)addr), PyInt_AsLong(PyTuple_GetItem(v,0)));
SetMicros( (*(mb_time *)addr), PyInt_AsLong(PyTuple_GetItem(v,0)));
} else {
PyErr_BadArgument();
return -1;
}
break;
case T_DATE:
if (PyString_Check(v)) {
*(mb_date *)addr = scn_date(PyString_AsString(v));
} else if (PyTuple_Check(v)) {
SetYear( (*(mb_date *)addr), PyInt_AsLong(PyTuple_GetItem(v,0)));
SetMonth((*(mb_date *)addr), PyInt_AsLong(PyTuple_GetItem(v,1)));
SetDay( (*(mb_date *)addr), PyInt_AsLong(PyTuple_GetItem(v,2)));
} else {
PyErr_BadArgument();
return -1;
}
break;
case T_PHONE:
if (PyString_Check(v)) {
scn_phone((mb_phone *)addr, PyString_AsString(v));
} else if (PyTuple_Check(v) &&
(PyTuple_Size(v) >= 2 && PyTuple_Size(v) <= 4)) {
((mb_phone *)addr)->area = 0;
((mb_phone *)addr)->prefix = 0;
((mb_phone *)addr)->number = 0;
((mb_phone *)addr)->ext = 0;
switch (PyTuple_Size(v)) {
case 4:
((mb_phone *)addr)->ext = PyInt_AsLong(PyTuple_GetItem(v,3));
case 3:
((mb_phone *)addr)->area = PyInt_AsLong(PyTuple_GetItem(v,0));
case 2:
((mb_phone *)addr)->prefix = PyInt_AsLong(PyTuple_GetItem(v,1));
((mb_phone *)addr)->number = PyInt_AsLong(PyTuple_GetItem(v,2));
}
} else {
PyErr_BadArgument();
return -1;
}
break;
case T_LONG:
case T_ULONG:
case T_SERIAL:
if (PyInt_Check(v))
if (l->type == T_LONG)
*(long*)addr = (long)PyInt_AsLong(v);
else
*(unsigned long*)addr =
(unsigned long)PyInt_AsLong(v);
else if (PyLong_Check(v))
if (l->type == T_LONG)
*(long*)addr = (long)PyLong_AsLong(v);
else
*(unsigned long*)addr =
(unsigned long)PyLong_AsLong(v);
else {
PyErr_BadArgument();
return -1;
}
break;
case T_FLOAT:
if (PyInt_Check(v))
*(float*)addr = (float)PyInt_AsLong(v);
else if (PyFloat_Check(v))
*(float*)addr = (float)PyFloat_AsDouble(v);
else {
PyErr_BadArgument();
return -1;
}
break;
case T_DOUBLE:
case T_MONEY:
if (PyInt_Check(v))
*(double*)addr = PyInt_AsLong(v);
else if (PyFloat_Check(v))
*(double*)addr = PyFloat_AsDouble(v);
else {
PyErr_BadArgument();
return -1;
}
break;
case T_BYTE:
if (!PyString_Check(v)) {
PyErr_BadArgument();
return -1;
}
memset((char *)addr,'\0',l->length);
memcpy((char *)addr,PyString_AsString(v),PyString_Size(v));
break;
case T_MCHAR:
case T_MBYTE:
{
int fld;
struct stat S;
unsigned long l_pos;
int count = 0;

fieldInit(rel, o_addr, l->fld_num, NULL);
fld = fieldOpen(rel, o_addr, l->fld_num);
if (fstat(fld,&S) == -1) {
PyErr_SetString(PyExc_IOError,sys_errlist[errno]);
close(fld);
return -1;
}
l_pos = lseek(fld,1,0);
while (write(fld,PyString_AsString(v),
PyString_Size(v))== -1) {
if (errno == EINTR) {
count++;
if (count > 10) {
PyErr_SetString(PyExc_IOError,sys_errlist[errno]);
close(fld);
return -1;
}
lseek(fld,0,l_pos);
} else {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
close(fld);
return -1;
}
}
close(fld);
}
break;
}
return 0;
}
}
PyErr_SetString(PyExc_AttributeError, name);
return -1;
}

static
int get_indx_num(rel, name)
relation *rel;
char *name;
{
int i;
for (i = 0; i < rel->nIdx; i++)
if (strcmp(name,rel->idxName[i]) == 0)
return i;
return -1;
}

static
int get_fld_num(rel, name)
relation *rel;
char *name;
{
int i;
for (i = 0; i < rel->nFld; i++)
if (strcmp(name,rel->fldName[i]) == 0)
return i;
return -1;
}

typedef struct {
OB_HEAD
relation *rel;
int index_num;
_PyMetalbaseRec_FieldListT *mb;
} PyMetalbaseDbObject;

typedef struct {
OB_HEAD
PyMetalbaseDbObject *db;
int rec_num;
char *buf;
int valid;
} PyMetalbaseRecObject;

extern PyTypeObject PyMetalbaseDb_Type; /* Really static, forward */
extern PyTypeObject PyMetalbaseRec_Type;

#define PyMetalbaseDb_Check(v) ((v)->ob_type == &PyMetalbaseDb_Type)
#define PyMetalbaseRec_Check(v) ((v)->ob_type == &PyMetalbaseRec_Type && ((PyMetalbaseRecObject *)v)->valid == TRUE)

static PyMetalbaseRecObject *
PyMetalbaseRec_New(db)
PyMetalbaseDbObject *db;
{
PyMetalbaseRecObject *xp;
xp = PyObject_NEW(PyMetalbaseRecObject, &PyMetalbaseRec_Type);
if (xp == NULL)
return NULL;
xp->db = db;
Py_INCREF(db);
xp->rec_num = -1;
xp->buf = malloc(db->rel->cbRecord);
bzero(xp->buf,db->rel->cbRecord);
xp->valid = TRUE;
if (xp->buf == NULL) {
Py_DECREF(xp);
return (PyMetalbaseRecObject *)PyErr_NoMemory();
}
if (db->rel->fMulti) {
recInit(db->rel,xp->buf);
recClean(db->rel,xp->buf);
}
return xp;
}

static PyMetalbaseDbObject *
PyMetalbaseDb_New()
{
PyMetalbaseDbObject *xp;
xp = PyObject_NEW(PyMetalbaseDbObject, &PyMetalbaseDb_Type);
if (xp == NULL)
return NULL;
xp->index_num = 0;
xp->rel = NULL;
xp->mb = NULL;
return xp;
}

static PyMetalbaseDbObject *
PyMetalbaseDb_Setup( db, rel )
PyMetalbaseDbObject *db;
relation *rel;
{
int cnt_i;
int offset = 0;
if (db == (PyMetalbaseDbObject *)NULL)
return(db);
db->rel = rel;
db->mb = (_PyMetalbaseRec_FieldListT *)
malloc(sizeof(_PyMetalbaseRec_FieldListT)*(db->rel->nFld + 1));
for (cnt_i = 0;cnt_i < db->rel->nFld; cnt_i++) {
db->mb[cnt_i].name = db->rel->fldName[cnt_i];
db->mb[cnt_i].offset = db->rel->cbStart[cnt_i];
db->mb[cnt_i].readonly = db->rel->fReadOnly;
db->mb[cnt_i].fld_num = cnt_i;
db->mb[cnt_i].type = db->rel->fldType[cnt_i];
db->mb[cnt_i].length = db->rel->cbLen[cnt_i];
if (db->rel->fldType[cnt_i] == T_SERIAL)
db->mb[cnt_i].readonly = TRUE;
}
db->mb[cnt_i].name = NULL;
db->mb[cnt_i].type = 0;
db->mb[cnt_i].offset = 0;
db->mb[cnt_i].length = 0;
db->mb[cnt_i].readonly = 0;
return(db);
}

/* Metaldb methods */

static void
PyMetalbaseDb_Dealloc(xp)
PyMetalbaseDbObject *xp;
{
if (xp->rel != NULL) {
mb_rmv(xp->rel);
}
if (xp->mb != NULL)
free(xp->mb);
PyMem_DEL(xp);
}

static PyObject *
PyMetalbaseDb_Number(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
int rtn;
if (!PyArg_NoArgs(args))
return NULL;
rtn = mb_num(self->rel);
if (rtn == -1) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
return (PyInt_FromLong(rtn));
}

static PyObject *
PyMetalbaseDb_Number_q(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
int rtn;
if (!PyArg_NoArgs(args))
return NULL;
rtn = mb_num_q(self->rel);
if (rtn == -1) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
return (PyInt_FromLong(rtn));
}

static PyObject *
PyMetalbaseDb_Describe(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
int count;
PyObject *rtn_tuple;
PyObject *schema_list;
PyObject *indx_list;

if (!PyArg_NoArgs(args))
return NULL;
rtn_tuple = PyTuple_New(2);
schema_list = newlistobject(self->rel->nFld);
for (count=0;count < self->rel->nFld; count++) {
PyObject *tmp_t = PyTuple_New(3);
PyObject *tmp_s = PyString_FromString(self->rel->fldName[count]);
PyObject *tmp_i1 = PyInt_FromLong(self->rel->fldType[count]);
PyObject *tmp_i2 = PyInt_FromLong(self->rel->cbLen[count]);

PyTuple_SetItem(tmp_t,0,tmp_s);
PyTuple_SetItem(tmp_t,1,tmp_i1);
PyTuple_SetItem(tmp_t,2,tmp_i2);
setlistitem(schema_list,count,tmp_t);
}
indx_list = newlistobject(self->rel->nIdx);
for (count=0;count < self->rel->nIdx; count++) {
PyObject *tmp_t = PyTuple_New(3);
PyObject *tmp_s1 =PyString_FromString(self->rel->idxName[count]);
PyObject *tmp_l;
PyObject *tmp_dups;
int index;
char cnt[4];
int i_cnt;
int i_cnt2;

PyTuple_SetItem(tmp_t,0,tmp_s1);

if (self->rel->fDups[count] == 1)
tmp_dups = PyString_FromString("dups");
else
tmp_dups = PyString_FromString("nodups");
PyTuple_SetItem(tmp_t,2,tmp_dups);

i_cnt = self->rel->nIdxFld[count];
tmp_l = newlistobject(i_cnt);
PyTuple_SetItem(tmp_t,1,tmp_l);

for (index=0;index < i_cnt; index++) {
PyObject *tmp_s2;

i_cnt2 = self->rel->idxFld[count][index];
tmp_s2 = PyString_FromString(self->rel->fldName[i_cnt2]);
setlistitem(tmp_l,index,tmp_s2);
}

setlistitem(indx_list,count,tmp_t);
}

PyTuple_SetItem(rtn_tuple,0,schema_list);
PyTuple_SetItem(rtn_tuple,1,indx_list);
return rtn_tuple;
}

static PyObject *
PyMetalbaseDb_Useindex(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
int index_num;
char *index_name;

if ( !PyArg_Parse(args,"s", &index_name) )
return NULL;
index_num = get_indx_num(self->rel,index_name);
if (index_num == -1) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
} else
self->index_num = index_num;
return (PyInt_FromLong(1));
}

static PyObject *
PyMetalbaseDb_Currindex(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
if (!PyArg_NoArgs(args))
return NULL;
return PyString_FromString(self->rel->idxName[self->index_num]);
}

static PyObject *
PyMetalbaseDb_Update(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
PyMetalbaseRecObject *rec;
mb_err rtn;

if ( !PyArg_Parse(args,"O", &rec) )
return NULL;
if (!PyMetalbaseRec_Check(rec)) {
PyErr_SetString(ValueError,"must be a PyMetalbaseRecObject object");
return NULL;
}
if (self != rec->db) {
PyErr_SetString(PyExc_TypeError,
"passed in record is not associated with this database");
return NULL;
}
self->rel->pos = rec->rec_num;
do
{
rtn = mb_upd(self->rel,rec->buf);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn != MB_OKAY) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
return PyInt_FromLong(1);
}

static PyObject *
PyMetalbaseDb_Delete(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
PyMetalbaseRecObject *rec;
mb_err rtn;

if ( !PyArg_Parse(args,"O", &rec) )
return NULL;
if (!PyMetalbaseRec_Check(rec)) {
PyErr_SetString(ValueError,"must be a PyMetalbaseRecObject object");
return NULL;
}
if (self != rec->db) {
PyErr_SetString(PyExc_TypeError,
"passed in record is not associated with this database");
return NULL;
}
self->rel->pos = rec->rec_num;
do
{
rtn = mb_del(self->rel);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn != MB_OKAY) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
rec->valid = FALSE;
return PyInt_FromLong(1);
}

static int
PyMetalbaseRec_Fill(rec,def_values,override)
PyMetalbaseRecObject *rec;
PyObject *def_values;
int override;
{
int cnt_i;
int index;
PyObject *value_t;
PyObject *field_name;
PyObject *value;

cnt_i = PyList_Size(def_values);
for (index = 0;index < cnt_i;index ++) {
value_t = PyList_GetItem(def_values,index);
if (!PyTuple_Check(value_t)) {
PyErr_SetString(PyExc_TypeError,"Elements in list must be tuples");
return -1;
}
if (PyTuple_Size(value_t)!= 2) {
PyErr_SetString(PyExc_TypeError,
"tuple must contain 2 items: (field_name,value)");
return -1;
}
field_name = PyTuple_GetItem(value_t,0);
value = PyTuple_GetItem(value_t,1);
if (!PyString_Check(field_name)) {
PyErr_SetString(PyExc_TypeError,"field_name must be a string");
return -1;
}
if (_PyMetalbaseRec_SetField(rec->db->rel, rec->buf, rec->db->mb,
PyString_AsString(field_name),
value, override) == -1) {
return -1;
}
}
return 0;
}

static PyObject *
PyMetalbaseDb_NewRec(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
PyObject *def_values;
PyMetalbaseRecObject *rec;
mb_err rtn;

if ( !PyArg_Parse(args,"O", &def_values) )
return NULL;
if (!PyList_Check(def_values)) {
PyErr_SetString(PyExc_TypeError,
"Argument must be a list of default values");
return NULL;
}
if (!(rec = PyMetalbaseRec_New(self))) {
return NULL;
}
if (PyMetalbaseRec_Fill(rec,def_values,FALSE) == -1) {
Py_DECREF(rec);
return NULL;
}
do
{
rtn = mb_add(rec->db->rel,rec->buf);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn != MB_OKAY) {
Py_DECREF(rec);
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
rec->rec_num = rec->db->rel->pos;
return ((PyObject *)rec);
}

static PyObject *
PyMetalbaseDb_NewRec_q(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
PyObject *def_values;
PyMetalbaseRecObject *rec;
mb_err rtn;

if ( !PyArg_Parse(args,"O", &def_values) )
return NULL;
if (!PyList_Check(def_values)) {
PyErr_SetString(PyExc_TypeError,
"Argument must be a list of default values");
return NULL;
}
if (!(rec = PyMetalbaseRec_New(self))) {
return NULL;
}
if (PyMetalbaseRec_Fill(rec,def_values,FALSE) == -1) {
Py_DECREF(rec);
return NULL;
}
do
{
rtn = mb_add_q(rec->db->rel,rec->buf);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn != MB_OKAY) {
Py_DECREF(rec);
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
rec->rec_num = rec->db->rel->pos;
return ((PyObject *)rec);
}

static PyObject *
PyMetalbaseDb_Search(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
PyObject *comp;
int act;
PyObject *def_values;
PyMetalbaseRecObject *rec;
PyMetalbaseRecObject *rtn_rec;
mb_err rtn;

if ( !PyArg_Parse(args,"(iO)", &act,&def_values) )
return NULL;
if (act < FRST || act > PQUE) {
PyErr_SetString(PyExc_TypeError,
"Action argument must be between FRST and PQUE");
return NULL;
}
if (PyList_Check(def_values)) {
int count;
rec = PyMetalbaseRec_New(self);
if (PyMetalbaseRec_Fill(rec,def_values,TRUE) == -1) {
Py_DECREF(rec);
return NULL;
}
} else if (PyMetalbaseRec_Check(def_values)) {
rec = (PyMetalbaseRecObject *)def_values;
Py_INCREF(rec);
self->rel->pos = rec->rec_num;
} else {
PyErr_SetString(PyExc_TypeError,
"You must pass in either a field list, or a record");
return NULL;
}
rtn_rec = PyMetalbaseRec_New(self);
do
{
rtn = mb_sel(self->rel, self->index_num,
rtn_rec->buf, act, rec->buf);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn == MB_NO_SUCH) {
Py_DECREF(rec);
Py_DECREF(rtn_rec);
Py_INCREF(Py_None);
return Py_None;
}
if (rtn != MB_OKAY) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
Py_DECREF(rtn_rec);
Py_DECREF(rec);
return NULL;
}
rtn_rec->rec_num = self->rel->pos;
Py_DECREF(rec);
return((PyObject *)rtn_rec);
}

static PyObject *
PyMetalbaseDb_Xfer(self, args)
PyMetalbaseDbObject *self;
PyObject *args;
{
int rtn;
if (!PyArg_NoArgs(args))
return NULL;
do
{
rtn = mb_xfer(self->rel);
} while (rtn == MB_LOCKED || rtn == MB_BUSY);
if (rtn == MB_OKAY) {
rtn = 1;
} else if (rtn == MB_NO_QUEUE) {
rtn = 0;
} else {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
return (PyInt_FromLong(rtn));
}

static PyMethodDef PyMetalbaseDb_Methods[] = {
{"number", (method)PyMetalbaseDb_Number},
{"number_q", (method)PyMetalbaseDb_Number_q},
{"number_queued", (method)PyMetalbaseDb_Number_q},
{"describe", (method)PyMetalbaseDb_Describe},
{"useindex", (method)PyMetalbaseDb_Useindex},
{"currindex", (method)PyMetalbaseDb_Currindex},
{"update", (method)PyMetalbaseDb_Update},
{"delete", (method)PyMetalbaseDb_Delete},
{"new", (method)PyMetalbaseDb_NewRec},
{"new_q", (method)PyMetalbaseDb_NewRec_q},
{"new_queued", (method)PyMetalbaseDb_NewRec_q},
{"search", (method)PyMetalbaseDb_Search},
{"index_q", (method)PyMetalbaseDb_Xfer},
{"index_queued", (method)PyMetalbaseDb_Xfer},
NULL, NULL /* sentinel */
};

static PyObject *
PyMetalbaseDb_GetAttr(self, name)
PyObject *self;
char *name;
{
return Py_FindMethod(PyMetalbaseDb_Methods, self, name);
}

static void
PyMetalbaseRec_Dealloc(xp)
PyMetalbaseRecObject *xp;
{
if (xp->db->rel->fMulti)
recFree(xp->db->rel, xp->buf);
if (xp->db != NULL)
Py_DECREF(xp->db);
if (xp->buf != NULL)
free(xp->buf);
/* Scan through each record and flush it if necessary */
PyMem_DEL(xp);
}

static PyObject *
PyMetalbaseRec_GetAttr(self, name)
PyMetalbaseRecObject *self;
char *name;
{
return _PyMetalbaseRec_GetField(self->db->rel,
self->buf,
self->db->mb,
name);
}

static int
PyMetalbaseRec_SetAttr(self, name, obj)
PyMetalbaseRecObject *self;
char *name;
PyObject *obj;
{
return _PyMetalbaseRec_SetField(self->db->rel,
self->buf,
self->db->mb,
name,
obj,
FALSE);
}

static PyTypeObject PyMetalbaseDb_Type = {
OB_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"PyMetalbaseDb", /*tp_name*/
sizeof(PyMetalbaseDbObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)PyMetalbaseDb_Dealloc, /*tp_dealloc*/
0, /*tp_print*/
(getattrfunc)PyMetalbaseDb_GetAttr, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash (hashfunc) */
};

static PyTypeObject PyMetalbaseRec_Type = {
OB_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"PyMetalbaseRec", /*tp_name*/
sizeof(PyMetalbaseRecObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)PyMetalbaseRec_Dealloc, /*tp_dealloc*/
0, /*tp_print*/
(getattrfunc)PyMetalbaseRec_GetAttr, /*tp_getattr*/
(setattrfunc)PyMetalbaseRec_SetAttr, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash (hashfunc)*/
};

static PyObject *
PyMetalbase_Open(self, args)
PyObject *self;
PyObject *args;
{
char *dir;
char *rel_name;
char *filename;
int rtn;
PyObject *rtn_o;
relation *rel_t = RNULL;

if ( !PyArg_Parse(args,"(ss)", &dir, &rel_name) )
return NULL;
filename = malloc(strlen(dir) + strlen(rel_name) + 10);
if (filename == NULL)
return PyErr_NoMemory();
sprintf(filename,"%s/%s", dir, rel_name);
rel_t = mb_inc(filename,0);
free(filename);
if (rel_t == RNULL) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
rtn_o = (PyObject *)PyMetalbaseDb_Setup(PyMetalbaseDb_New(), rel_t);
return rtn_o;
}

static PyObject *
PyMetalbase_Create(self, args)
PyObject *self;
PyObject *args;
{
char *dir;
char *rel_name;
char *filename;
int rtn;
PyObject *schema;
PyObject *indx;
relation *rel;
int len;
int count;
static char err_str[80];

if ( !PyArg_Parse(args,"(ssOO)", &dir, &rel_name, &schema, &indx) )
return NULL;
rel = mb_new(); /* Create relation to store information in */
if (rel == NULL)
goto do_error;

if (!PyList_Check(schema)) {
PyErr_SetString(PyExc_TypeError,"Schema definition must be a list");
mb_rmv(rel);
return NULL;
}
if (!PyList_Check(indx)) {
PyErr_SetString(PyExc_TypeError,"Index definition must be a list");
mb_rmv(rel);
return NULL;
}
len = PyList_Size(schema);
if (len == 0) {
PyErr_SetString(PyExc_TypeError,"Schema must contain at least one field");
mb_rmv(rel);
return NULL;
}
if (len > MAX_FLD) {
sprintf(err_str,"Too many fields: %d. Only %d fields allowed.",
len, MAX_FLD);
PyErr_SetString(PyMetalbase_ExcError,err_str);
mb_rmv(rel);
return NULL;
}
/* use schema to create field list */
count = 0;
while (count < len) {
PyObject *tuple;
PyObject *name;
PyObject *type;
PyObject *arg;
char *name_val;
int type_val;
int arg_val;
static char err_str[80];

if (!PyTuple_Check(PyList_GetItem(schema,count))) {
sprintf(err_str,"Field %d is not a tuple.",
count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
tuple = PyList_GetItem(schema,count);
if (PyTuple_Size(tuple) != 3) {
sprintf(err_str,"Field %d does not contain 3 elements.",
count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
name = PyTuple_GetItem(tuple,0);
type = PyTuple_GetItem(tuple,1);
arg = PyTuple_GetItem(tuple,2);
if (!PyString_Check(name)) {
sprintf(err_str,"Field %d's fieldname element is not a string.",
count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
name_val = PyString_AsString(name);
if (get_fld_num(rel,name_val) != -1) {
sprintf(err_str,"Field '%s' already exists. Cannot add twice.",
name_val);
PyErr_SetString(PyMetalbase_ExcError,err_str);
mb_rmv(rel);
return NULL;
}
if (!PyInt_Check(type)) {
sprintf(err_str,"Field '%s' does not have an integer as the type.",
name_val);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
type_val = PyInt_AsLong(type);
if (type_val < T_CHAR || type_val > T_LASTTYPE) {
sprintf(err_str,"Field '%s' does not have a valid type.",
name_val);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
if (!PyInt_Check(arg)) {
sprintf(err_str,"Field '%s' does not have an integer as the argument.",
name_val);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
arg_val = PyInt_AsLong(arg);
if (mb_addfield(rel,name_val,type_val,arg_val) != MB_OKAY) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
mb_rmv(rel);
return NULL;
}
count++;
}

len = PyList_Size(indx);
if (len == 0) {
PyErr_SetString(PyExc_TypeError,
"Index list must contain at least one entry");
mb_rmv(rel);
return NULL;
}
if (len > MAX_IDX) {
sprintf(err_str,"Too many indexes: %d. Only %d indexes allowed.",
len, MAX_IDX);
PyErr_SetString(PyMetalbase_ExcError,err_str);
mb_rmv(rel);
return NULL;
}
/* use indx to create index list */
count = 0;
while (count < len) {
PyObject *tuple;
PyObject *name;
PyObject *fieldlist;
int dups;
char *name_val;
char *fieldstr;
int count2;
int count3;
char *field_name;

if (!PyTuple_Check(PyList_GetItem(indx,count))) {
sprintf(err_str,"Index %d is not a tuple.",count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
tuple = PyList_GetItem(indx,count);
dups = 1;
switch (PyTuple_Size(tuple)) {
case 3:
if (!PyString_Check(PyTuple_GetItem(tuple,2))) {
sprintf(err_str,"Third element of Index %d must be a string",count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
{
char *dp;
dp = PyString_AsString(PyTuple_GetItem(tuple,2));
if (strcmp(dp, "dups") == 0)
dups = 1;
else
dups = 0;
}
case 2:
if (!PyString_Check(PyTuple_GetItem(tuple,0))) {
sprintf(err_str,"First element of Index %d must be a string", count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
if (!PyList_Check(PyTuple_GetItem(tuple,1))) {
sprintf(err_str,"Second element of Index %d must be a list", count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
name = PyTuple_GetItem(tuple,0);
fieldlist = PyTuple_GetItem(tuple,1);
name_val = PyString_AsString(name);
break;
default:
sprintf(err_str,"Index %d not formatted correctly.", count);
PyErr_SetString(PyExc_TypeError,err_str);
mb_rmv(rel);
return NULL;
}
count2 = PyList_Size(fieldlist);
fieldstr = malloc( (4*count2) + 5);
if (fieldstr == NULL) {
mb_rmv(rel);
return PyErr_NoMemory();
}
fieldstr[0] = '\0';
for (count3 = 0;count3 < count2;count3++) {
int field_num;
PyObject *field_o;
char tmp[20];
char *field_str;
field_o = PyList_GetItem(fieldlist,count3);
if (!PyString_Check(field_o)) {
sprintf(err_str,
"Index '%s': element %d of fieldname list is not a string.",
name_val, count3);
PyErr_SetString(PyExc_TypeError,err_str);
free(fieldstr);
mb_rmv(rel);
return NULL;
}
field_str = PyString_AsString(field_o);
field_num = get_fld_num(rel,field_str);
if (field_num == -1) {
sprintf(err_str,"Index '%s' has invalid fieldname '%s'.",
name_val,field_str);
PyErr_SetString(PyExc_TypeError,err_str);
free(fieldstr);
mb_rmv(rel);
return NULL;
}
if (count3 != 0)
strcat(fieldstr,",");
sprintf(tmp,"%d",field_num);
strcat(fieldstr,tmp);
}
if (mb_addindex(rel,name_val,dups,fieldstr) != MB_OKAY) {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
free(fieldstr);
mb_rmv(rel);
return NULL;
}
free(fieldstr);
count++;
}

filename = malloc(strlen(dir) + strlen(rel_name) + 15);
if (filename == NULL) {
mb_rmv(rel);
return PyErr_NoMemory();
}
sprintf(filename,"%s/%s", dir, rel_name);
if (strcmp(&filename[strlen(filename)-3],".rel"))
strcat(filename,".rel");
rtn = mb_create(rel,filename,0); /* Actually create file */
free(filename);
mb_rmv(rel); /* Delete temporary storage in relation */
if (rtn == MB_OKAY) {
Py_INCREF(Py_None);
return Py_None;
} else {
do_error:
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
}

static PyObject *
PyMetalbase_Test(self, args)
PyObject *self;
PyObject *args;
{
char *dir;
char *rel_name;
char *filename;
int rtn;

if ( !PyArg_Parse(args,"(ss)", &dir, &rel_name) )
return NULL;
filename = malloc(strlen(dir) + strlen(rel_name) + 10);
if (filename == NULL)
return PyErr_NoMemory();
sprintf(filename,"%s/%s", dir, rel_name);
rtn = mb_tst(filename);
free(filename);
if (rtn == 0) {
Py_INCREF(Py_None);
return Py_None;
} else {
PyErr_SetString(PyMetalbase_ExcError,mb_error);
return NULL;
}
}

/* List of functions defined in the module */

static PyMethodDef PyMetalbase_Methods[] = {
{"open", PyMetalbase_Open},
{"create", PyMetalbase_Create},
{"test", PyMetalbase_Test},
NULL, NULL /* sentinel */
};

/* Initialization function for the module (*must* be called initxx) */

static void init2(PyObject *m, PyObject *d)
{
PyObject *x;

PyDict_SetItemString(d, "error", PyMetalbase_ExcError);

/* These are for the Types of fields */
x = PyInt_FromLong(T_CHAR);
PyDict_SetItemString(d, "CHAR", x);
x = PyInt_FromLong(T_SHORT);
PyDict_SetItemString(d, "SHORT", x);
x = PyInt_FromLong(T_USHORT);
PyDict_SetItemString(d, "USHORT", x);
x = PyInt_FromLong(T_LONG);
PyDict_SetItemString(d, "LONG", x);
x = PyInt_FromLong(T_ULONG);
PyDict_SetItemString(d, "ULONG", x);
x = PyInt_FromLong(T_FLOAT);
PyDict_SetItemString(d, "FLOAT", x);
x = PyInt_FromLong(T_DOUBLE);
PyDict_SetItemString(d, "DOUBLE", x);
x = PyInt_FromLong(T_MONEY);
PyDict_SetItemString(d, "MONEY", x);
x = PyInt_FromLong(T_TIME);
PyDict_SetItemString(d, "TIME", x);
x = PyInt_FromLong(T_DATE);
PyDict_SetItemString(d, "DATE", x);
x = PyInt_FromLong(T_SERIAL);
PyDict_SetItemString(d, "SERIAL", x);
x = PyInt_FromLong(T_PHONE);
PyDict_SetItemString(d, "PHONE", x);
x = PyInt_FromLong(T_BYTE);
PyDict_SetItemString(d, "BYTE", x);
x = PyInt_FromLong(T_MCHAR);
PyDict_SetItemString(d, "MCHAR", x);
x = PyInt_FromLong(T_MBYTE);
PyDict_SetItemString(d, "MBYTE", x);

/* Search modifiers */
x = PyInt_FromLong(FRST);
PyDict_SetItemString(d, "FRST", x);
PyDict_SetItemString(d, "FIRST", x);
x = PyInt_FromLong(LAST);
PyDict_SetItemString(d, "LAST", x);
x = PyInt_FromLong(CURR);
PyDict_SetItemString(d, "CURR", x);
PyDict_SetItemString(d, "CURRENT", x);
x = PyInt_FromLong(NEXT);
PyDict_SetItemString(d, "NEXT", x);
x = PyInt_FromLong(PREV);
PyDict_SetItemString(d, "PREV", x);
PyDict_SetItemString(d, "PREVIOUS", x);
x = PyInt_FromLong(GTEQ);
PyDict_SetItemString(d, "GTEQ", x);
x = PyInt_FromLong(GTHN);
PyDict_SetItemString(d, "GTHN", x);
PyDict_SetItemString(d, "GTHAN", x);
x = PyInt_FromLong(LTEQ);
PyDict_SetItemString(d, "LTEQ", x);
x = PyInt_FromLong(LTHN);
PyDict_SetItemString(d, "LTHN", x);
PyDict_SetItemString(d, "LTHAN", x);
x = PyInt_FromLong(EQUL);
PyDict_SetItemString(d, "EQUL", x);
PyDict_SetItemString(d, "EQUAL", x);
x = PyInt_FromLong(FQUE);
PyDict_SetItemString(d, "FQUE", x);
PyDict_SetItemString(d, "FIRST_Q", x);
x = PyInt_FromLong(LQUE);
PyDict_SetItemString(d, "LQUE", x);
PyDict_SetItemString(d, "LAST_Q", x);
x = PyInt_FromLong(PQUE);
PyDict_SetItemString(d, "PQUE", x);
PyDict_SetItemString(d, "PREVIOUS_Q", x);
x = PyInt_FromLong(NQUE);
PyDict_SetItemString(d, "NQUE", x);
PyDict_SetItemString(d, "NEXT_Q", x);

x = PyString_FromString("nodups");
PyDict_SetItemString(d, "NODUPS", x);
x = PyString_FromString("dups");
PyDict_SetItemString(d, "DUPS", x);

/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module metalbase");
}

void
initmetalbase()
{
PyObject *m, *d;

/* Create the module and add the functions */
m = Py_InitModule("metalbase", PyMetalbase_Methods);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyMetalbase_ExcError = PyString_FromString("metalbase.error");
init2( m, d );
}

void
initmbase()
{
PyObject *m, *d;

/* Create the module and add the functions */
m = Py_InitModule("mbase", PyMetalbase_Methods);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyMetalbase_ExcError = PyString_FromString("mbase.error");
init2( m, d );
}

void
initmb()
{
PyObject *m, *d;

/* Create the module and add the functions */
m = Py_InitModule("mb", PyMetalbase_Methods);
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
PyMetalbase_ExcError = PyString_FromString("mb.error");
init2( m, d );
}

--
Lance Ellinghouse                lance@fox.com