#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "cdb.h"
#ifdef __cplusplus
}
#endif
/* alias */
#define DOOPEN 1
#define DOCREATE 2
#define DOUPDATE 4
#define DOLOAD 8
#define WITHTEMP (DOCREATE | DOUPDATE)
#define WITHOPEN (DOOPEN | DOUPDATE | DOLOAD)
#define READONLY (DOOPEN | DOLOAD)
/* opts */
#define EACH_INITIALIZED 1
#define COMMITTED 2
#define DIED 4
/* get mode */
#define DOGETALL 1
#define DOGETLAST 2
/* methods */
#define METHOD_GET 1
#define METHOD_GETALL 2
#define METHOD_KEYS 4
#define METHOD_EACH 8
#define METHOD_ADD 16
#define METHOD_INSERT 32
#define METHOD_EXISTS 64
#define METHOD_FINISH 128
/* <cdb_make_free is not public - copy from cdb_ini.h and cdb_make.c> */
struct cdb_rec {
unsigned hval;
unsigned rpos;
};
struct cdb_rl {
struct cdb_rl *next;
unsigned cnt;
struct cdb_rec rec[254];
};
static void
cdb_make_free(struct cdb_make *cdbmp)
{
unsigned t;
for(t = 0; t < 256; ++t) {
struct cdb_rl *rl = cdbmp->cdb_rec[t];
while(rl) {
struct cdb_rl *tm = rl;
rl = rl->next;
free(tm);
}
}
}
/* </cdb_make_free is not public - copy from cdb_int.h and cdb_make.c> */
#define GROWIFNEEDED(self, var, len, bufsize) if (len > bufsize) { \
Renew(var, len + 1, char); \
if ( var == NULL ) { \
memerror(self, len + 1); \
}; \
bufsize = len; \
};
#define MAKESV(var, len) SV *var = sv_newmortal(); \
SvUPGRADE( var, SVt_PV ); \
(void)SvPOK_only( var ); \
SvGROW( var, len + 1 ); \
SvCUR_set( var, len );
#define PUSHWITHNULL(var,len) SvPV(var, PL_na)[len] = '\0'; \
XPUSHs( var );
#define ALREADY_COMMITTED croak("Database changes already committed")
#define READ_ONLY_MODE croak("Database opened in read only mode")
#define FATAL_ERROR croak("Database unstable - cannot continue")
#define CREATE_ONLY_MODE croak("Database opened in create only mode")
struct t_cdb {
char *fn; /* file name */
char *fntemp; /* tempfile name */
PerlIO *fd; /* file descriptor */
PerlIO *fdtemp; /* tempfile descriptor */
struct cdb cdb; /* cdb struct */
struct cdb_make cdbm; /* cdb_make struct */
int alias; /* summoned to do */
int opts; /* options */
unsigned int curpos; /* current position in file */
int dend; /* end position in file */
struct { /* container for allocated memory */
unsigned char *buf;
char *key;
char *val;
} mem;
};
typedef struct t_cdb CDB_TinyCDB;
static void memfree ( CDB_TinyCDB * self ) {
if ( self->mem.buf ) { Safefree(self->mem.buf); self->mem.buf = 0; }
if ( self->mem.key ) { Safefree(self->mem.key); self->mem.key = 0; }
if ( self->mem.val ) { Safefree(self->mem.val); self->mem.val = 0; }
}
static void fileerror(CDB_TinyCDB * self, char *op, char *fn) {
memfree(self);
self->opts |= DIED;
croak("Unable to %s file %s: %s", op, fn, Strerror(errno));
}
static void memerror(CDB_TinyCDB * self, const int size) {
memfree(self);
self->opts |= DIED;
croak("Unable to allocate %d bytes of memory", size);
}
static void assert_status(CDB_TinyCDB * self, const int method) {
if ( self->opts & DIED ) FATAL_ERROR;
switch (method) {
case METHOD_GET:
case METHOD_GETALL:
case METHOD_KEYS:
case METHOD_EACH:
if (self->alias == DOCREATE) {
CREATE_ONLY_MODE;
}
break;
case METHOD_ADD:
case METHOD_INSERT:
if ( self->alias & WITHTEMP ) {
if ( self->opts & COMMITTED ) {
ALREADY_COMMITTED;
}
} else {
READ_ONLY_MODE;
}
break;
case METHOD_FINISH:
if ( ! ( self->alias & WITHTEMP ) ) {
READ_ONLY_MODE;
}
break;
case METHOD_EXISTS:
if (self->alias == DOCREATE && self->opts & COMMITTED) {
ALREADY_COMMITTED;
}
break;
}
}
static void commit( CDB_TinyCDB * self, const int save_changes, const int reopen ) {
if (self->opts & COMMITTED) return;
self->opts |= COMMITTED;
if ( self->alias & WITHOPEN ) {
if (self->alias & DOLOAD) {
cdb_free(&self->cdb);
}
if ( PerlIO_error( self->fd ) ) {
fileerror(self, "close", self->fn);
}
PerlIO_close(self->fd);
}
if ( self->alias & WITHTEMP ) {
if ( save_changes ) {
if ( cdb_make_finish(&self->cdbm) != 0 ) {
fileerror(self, "commit changes", self->fntemp);
};
} else {
cdb_make_free(&self->cdbm);
};
if ( PerlIO_close(self->fdtemp) != 0 ) {
fileerror(self, "close", self->fntemp);
};
if ( save_changes ) {
if ( rename(self->fntemp, self->fn) != 0 ) {
fileerror(self, "replace", self->fn);
};
if ( reopen && ! (self->alias == DOCREATE)) {
self->fd = PerlIO_open(self->fn, "rb");
if ( ! self->fd ) {
fileerror(self, "reopen", self->fn);
}
if (self->alias & DOLOAD) { /* mmap to memory whole file */
cdb_init(&self->cdb, PerlIO_fileno( self->fd ));
}
}
} else {
if ( unlink(self->fntemp) != 0 ) {
fileerror(self, "unlink", self->fntemp);
};
}
};
}
static int perlio_bread(PerlIO *fd, void *buf, int len) {
int l;
while (len > 0) {
do {
l = PerlIO_read(fd, buf, len);
} while (l < 0 && errno == EINTR);
if (l <= 0) {
if (!l) {
errno = EIO;
}
return -1;
}
buf = (char*)buf + l;
len -= l;
}
return 0;
}
MODULE = CDB::TinyCDB PACKAGE = CDB::TinyCDB
PROTOTYPES: ENABLE
CDB_TinyCDB *
open(CLASS, ...)
char * CLASS
ALIAS:
load = DOLOAD
INIT:
if ( sv_isobject( ST(0) ) && (SvTYPE(SvRV(ST(0))) == SVt_PVMG) ) {
croak("%s is already blessed\n", SvPV(ST(0), PL_na));
}
CODE:
{
int mode = 0;
if ( items == 4) {
char *for_method = SvPV( ST(2), PL_na);
if ( strEQ(for_method, "for_update") ) {
mode |= DOUPDATE;
} else {
croak("Invalid mode %s", for_method);
}
} else if ( items != 2 ) {
croak("Invalid numbers of arguments");
}
Newx(RETVAL, 1, CDB_TinyCDB);
RETVAL->alias = mode | ( ix ? ix : DOOPEN );
RETVAL->fn = savepv( SvPV( ST(1), PL_na ) );
RETVAL->fntemp = 0;
RETVAL->curpos = 0;
RETVAL->opts = 0;
RETVAL->mem.buf = 0;
RETVAL->mem.key = 0;
RETVAL->mem.val = 0;
if ( RETVAL->alias & WITHOPEN ) {
RETVAL->fd = PerlIO_open(RETVAL->fn, "rb");
if ( ! RETVAL->fd ) {
fileerror(RETVAL, "open", RETVAL->fn);
}
if (RETVAL->alias & DOLOAD) {
cdb_init(&RETVAL->cdb, PerlIO_fileno( RETVAL->fd ));
}
}
if (RETVAL->alias & WITHTEMP) {
RETVAL->fntemp = savepv( SvPV( ST(3), PL_na ) );
RETVAL->fdtemp = PerlIO_open(RETVAL->fntemp, "w+b");
if ( ! RETVAL->fdtemp ) {
fileerror(RETVAL, "create", RETVAL->fn);
};
cdb_make_start(&RETVAL->cdbm, PerlIO_fileno( RETVAL->fdtemp ));
unsigned int kbufsize = 2048, vbufsize = 2048;
unsigned int klen = 0, vlen = 0, curpos = 0;
Newx(RETVAL->mem.key, kbufsize + 1, char);
Newx(RETVAL->mem.val, vbufsize + 1, char);
if (RETVAL->alias & DOUPDATE) {
if (RETVAL->alias & DOLOAD) {
cdb_seqinit( &curpos, &RETVAL->cdb );
while ( cdb_seqnext(&curpos, &RETVAL->cdb) > 0 ) {
klen = cdb_keylen( &RETVAL->cdb );
vlen = cdb_datalen( &RETVAL->cdb );
GROWIFNEEDED( RETVAL, RETVAL->mem.key, klen, kbufsize );
GROWIFNEEDED( RETVAL, RETVAL->mem.val, vlen, vbufsize );
cdb_read( &RETVAL->cdb, RETVAL->mem.key, klen, cdb_keypos(&RETVAL->cdb) );
cdb_read( &RETVAL->cdb, RETVAL->mem.val, vlen, cdb_datapos(&RETVAL->cdb) );
if ( cdb_make_add(&RETVAL->cdbm,
RETVAL->mem.key, klen,
RETVAL->mem.val, vlen
) < 0
) {
fileerror(RETVAL, "update", RETVAL->fntemp);
}
}
} else {
unsigned int bytes, dend;
Newx(RETVAL->mem.buf, 2048, unsigned char);
PerlIO_rewind( RETVAL->fd );
bytes = PerlIO_read( RETVAL->fd, RETVAL->mem.buf, 2048 );
if ( bytes == 2048 ) {
dend = cdb_unpack(RETVAL->mem.buf);
curpos += bytes;
while ( curpos < dend - 8) {
bytes = PerlIO_read( RETVAL->fd, RETVAL->mem.buf, 8 );
if ( bytes != 8 ) {
fileerror(RETVAL, "read", RETVAL->fn);
}
curpos += bytes;
klen = cdb_unpack(RETVAL->mem.buf);
vlen = cdb_unpack(RETVAL->mem.buf + 4);
if (dend - klen < curpos || dend - vlen < curpos + klen) {
fileerror(RETVAL, "read", RETVAL->fn);
}
GROWIFNEEDED( RETVAL, RETVAL->mem.key, klen, kbufsize );
bytes = PerlIO_read( RETVAL->fd, RETVAL->mem.key, klen );
if (bytes != klen) {
fileerror(RETVAL, "read", RETVAL->fn);
};
curpos += bytes;
GROWIFNEEDED( RETVAL, RETVAL->mem.val, vlen, vbufsize );
bytes = PerlIO_read( RETVAL->fd, RETVAL->mem.val, vlen );
if (bytes != vlen) {
fileerror(RETVAL, "read", RETVAL->fn);
};
curpos += bytes;
if ( cdb_make_add(&RETVAL->cdbm,
RETVAL->mem.key, klen,
RETVAL->mem.val, vlen
) < 0
) {
fileerror(RETVAL, "update", RETVAL->fntemp);
}
};
} else {
fileerror(RETVAL, "read", RETVAL->fn);
}
PerlIO_rewind( RETVAL->fd );
if ( PerlIO_error( RETVAL->fd ) )
fileerror(RETVAL, "set position", RETVAL->fn);
}
}
}
memfree( RETVAL );
}
OUTPUT:
RETVAL
CDB_TinyCDB *
create(CLASS, fn, fntemp)
char * CLASS
char * fn
char * fntemp
INIT:
if ( sv_isobject( ST(0) ) && (SvTYPE(SvRV(ST(0))) == SVt_PVMG )) {
croak("%s is already blessed\n", SvPV(ST(0), PL_na));
}
CODE:
{
Newx(RETVAL, 1, CDB_TinyCDB);
RETVAL->alias = DOCREATE;
RETVAL->fn = savepv( fn );
RETVAL->fntemp = savepv( fntemp );
RETVAL->curpos = 0;
RETVAL->opts = 0;
RETVAL->mem.buf = 0;
RETVAL->mem.key = 0;
RETVAL->mem.val = 0;
RETVAL->fdtemp = PerlIO_open(fntemp, "w+b");
if ( ! RETVAL->fdtemp ) {
fileerror(RETVAL, "create", fn);
};
cdb_make_start(&RETVAL->cdbm, PerlIO_fileno( RETVAL->fdtemp ));
}
OUTPUT:
RETVAL
void
get(self, key)
CDB_TinyCDB *self
char *key
INIT:
assert_status( self, METHOD_GET );
PPCODE:
{
unsigned int vlen = 0;
STRLEN klen = strlen(key);
if (self->alias & DOLOAD) { /* tinyfile whole in memory */
if (cdb_find(&self->cdb, key, klen) > 0) {
vlen = cdb_datalen( &self->cdb ); /* length of data */
MAKESV( val, vlen );
if ( cdb_read( &self->cdb, SvPVX(val), vlen, cdb_datapos( &self->cdb )) < 0 ) {
fileerror(self, "read", self->fn);
};
PUSHWITHNULL( val, vlen );
};
} else {
if ( cdb_seek(PerlIO_fileno(self->fd), key, klen, &vlen) > 0 ) {
MAKESV( val, vlen );
if ( cdb_bread( PerlIO_fileno(self->fd), SvPVX(val), vlen ) < 0) {
fileerror(self, "read", self->fn);
};
XPUSHs( sv_2mortal(newSVpvn(self->mem.val, vlen)) );
PUSHWITHNULL( val, vlen );
};
};
}
int
exists(self, key)
CDB_TinyCDB *self
char *key
INIT:
assert_status( self, METHOD_EXISTS );
CODE:
{
STRLEN klen = strlen(key);
if (self->alias & WITHTEMP && !( self->opts & COMMITTED)) { /* for_create | for_update | create */
RETVAL = cdb_make_exists(&self->cdbm, key, klen);
if ( RETVAL < 0 ) {
fileerror(self, "read", self->fntemp);
}
} else {
if (self->alias & DOLOAD) { /* tinyfile whole in memory */
RETVAL = cdb_find(&self->cdb, key, klen);
} else {
unsigned int vlen;
RETVAL = cdb_seek(PerlIO_fileno(self->fd), key, klen, &vlen);
};
if ( RETVAL < 0 ) {
fileerror(self, "read", self->fn);
}
}
}
OUTPUT:
RETVAL
void
getall(self, key)
CDB_TinyCDB *self
char *key
ALIAS:
getlast = DOGETLAST
INIT:
assert_status( self, METHOD_GETALL );
PPCODE:
{
unsigned int kbufsize = 2048;
unsigned int klen = 0, vlen = 0;
unsigned int lastpos = 0, lastvlen = 0;
STRLEN searchklen = strlen(key);
int mode = ix ? ix : DOGETALL;
if (self->alias & DOLOAD) { /* tinyfile whole in memory */
struct cdb_find cdbf;
cdb_findinit( &cdbf, &self->cdb, key, searchklen );
while ( cdb_findnext(&cdbf) > 0 ) {
vlen = cdb_datalen(&self->cdb); /* length of data */
lastpos = cdb_datapos(&self->cdb);
if ( mode == DOGETALL ) {
MAKESV( val, vlen );
if (cdb_read(&self->cdb, SvPVX(val), vlen, lastpos) < 0 ) {
fileerror(self, "read", self->fn);
}
PUSHWITHNULL( val, vlen );
}
}
if ( mode == DOGETLAST && lastpos ) {
MAKESV( val, vlen );
if (cdb_read(&self->cdb, SvPVX(val), vlen, lastpos) < 0 ) {
fileerror(self, "read", self->fn);
}
PUSHWITHNULL( val, vlen );
}
} else { /* open */
unsigned int bytes, dend, curpos = 0;
Off_t prevpos = PerlIO_tell( self->fd );
Newx(self->mem.buf, kbufsize + 1, unsigned char); /* allocate memory */
Newx(self->mem.key, kbufsize + 1, char); /* allocate memory */
PerlIO_rewind( self->fd );
bytes = PerlIO_read( self->fd, self->mem.buf, 2048 );
if ( bytes == 2048 ) {
dend = cdb_unpack(self->mem.buf);
curpos += bytes;
while ( curpos < dend - 8) {
bytes = PerlIO_read( self->fd, self->mem.buf, 8 );
if ( bytes != 8 )
fileerror(self, "read", self->fn);
curpos += bytes;
klen = cdb_unpack(self->mem.buf);
vlen = cdb_unpack(self->mem.buf + 4);
if (dend - klen < curpos || dend - vlen < curpos + klen)
fileerror(self, "read", self->fn);
GROWIFNEEDED( self, self->mem.key, klen, kbufsize );
bytes = PerlIO_read( self->fd, self->mem.key, klen );
if (bytes != klen) {
fileerror(self, "read", self->fn);
};
curpos += bytes;
self->mem.key[klen] = '\0';
if ( klen == searchklen
&& strnEQ( self->mem.key, key, klen )
) {
lastpos = curpos;
lastvlen = vlen;
if ( mode == DOGETALL ) {
MAKESV( val, vlen );
if ( perlio_bread( self->fd, SvPVX(val), vlen ) < 0) {
fileerror(self, "read", self->fn);
};
PUSHWITHNULL( val, vlen );
} else {
PerlIO_seek(self->fd, vlen, SEEK_CUR);
}
} else {
PerlIO_seek(self->fd, vlen, SEEK_CUR);
}
curpos += vlen;
};
} else {
fileerror(self, "read", self->fn);
}
if ( mode == DOGETLAST && lastpos ) {
PerlIO_seek( self->fd, lastpos, SEEK_SET );
MAKESV( val, lastvlen );
if ( perlio_bread( self->fd, SvPVX(val), lastvlen ) < 0) {
fileerror(self, "read", self->fn);
};
PUSHWITHNULL( val, lastvlen );
}
/* go back to original position in file */
PerlIO_seek( self->fd, prevpos, SEEK_SET );
if ( PerlIO_error( self->fd ) )
fileerror(self, "set position", self->fn);
}
memfree( self );
}
void
each(self)
CDB_TinyCDB *self
INIT:
assert_status( self, METHOD_EACH );
PPCODE:
{
unsigned int klen = 0, vlen = 0;
unsigned int kbufsize = 2048;
int keep_looping = 1;
if ( self->alias & DOLOAD ) { /* load */
if ( !( self->opts & EACH_INITIALIZED ) ) {
self->curpos = 0;
cdb_seqinit( &self->curpos, &self->cdb );
self->opts |= EACH_INITIALIZED;
}
while ( keep_looping-- ) {
if ( cdb_seqnext(&self->curpos, &self->cdb) > 0 ) {
klen = cdb_keylen( &self->cdb );
vlen = cdb_datalen( &self->cdb );
if ( klen ) {
MAKESV( key, klen );
cdb_read( &self->cdb, SvPVX(key), klen, cdb_keypos(&self->cdb) );
PUSHWITHNULL( key, klen );
MAKESV( val, vlen );
cdb_read( &self->cdb, SvPVX(val), vlen, cdb_datapos(&self->cdb) );
PUSHWITHNULL( val, vlen );
} else {
keep_looping++;
}
} else {
self->opts &= ~EACH_INITIALIZED;
}
}
} else { /* open */
unsigned int bytes;
unsigned int klen, vlen;
Newx(self->mem.buf, kbufsize + 1, unsigned char); /* allocate memory */
if ( !( self->opts & EACH_INITIALIZED ) ) {
self->curpos = 0;
PerlIO_rewind( self->fd );
self->opts |= EACH_INITIALIZED;
bytes = PerlIO_read( self->fd, self->mem.buf, 2048 );
if ( bytes == 2048 ) {
self->dend = cdb_unpack(self->mem.buf);
} else {
fileerror(self, "read", self->fn);
}
self->curpos += bytes;
}
while ( keep_looping-- ) {
if ( self->curpos < self->dend - 8) {
bytes = PerlIO_read( self->fd, self->mem.buf, 8 );
if ( bytes != 8 ) {
fileerror(self, "read", self->fn);
}
self->curpos += bytes;
klen = cdb_unpack(self->mem.buf);
vlen = cdb_unpack(self->mem.buf + 4);
if (self->dend - klen < self->curpos || self->dend - vlen < self->curpos + klen)
fileerror(self, "read", self->fn);
if ( klen ) {
MAKESV( key, klen );
if ( perlio_bread( self->fd, SvPVX(key), klen ) < 0) {
fileerror(self, "read", self->fn);
};
self->curpos += klen;
PUSHWITHNULL( key, klen );
MAKESV( val, vlen );
if ( perlio_bread( self->fd, SvPVX(val), vlen ) < 0) {
fileerror(self, "read", self->fn);
};
self->curpos += vlen;
PUSHWITHNULL( val, vlen );
} else {
/* skip nulled out records (from replace0) */
self->curpos += klen + vlen;
PerlIO_seek(self->fd, klen + vlen, SEEK_CUR);
keep_looping++;
}
} else {
self->opts &= ~EACH_INITIALIZED;
};
}
if ( PerlIO_error( self->fd ) )
fileerror(self, "close", self->fn);
}
memfree( self );
}
void
keys(self)
CDB_TinyCDB *self
INIT:
assert_status( self, METHOD_KEYS );
PPCODE:
{
unsigned int curpos = 0;
unsigned int klen = 0, kbufsize = 2048;
if ( self->alias & DOLOAD ) { /* load */
cdb_seqinit( &curpos, &self->cdb );
while ( cdb_seqnext(&curpos, &self->cdb) > 0 ) {
klen = cdb_keylen( &self->cdb );
if ( ! klen ) continue;
MAKESV( key, klen );
cdb_read( &self->cdb, SvPVX(key), klen, cdb_keypos(&self->cdb) );
PUSHWITHNULL( key, klen );
}
} else { /* open */
unsigned int bytes, dend;
unsigned int klen, vlen;
Newx(self->mem.buf, kbufsize + 1, unsigned char); /* allocate memory */
Off_t prevpos = PerlIO_tell( self->fd );
PerlIO_rewind( self->fd );
bytes = PerlIO_read( self->fd, self->mem.buf, 2048 );
if ( bytes == 2048 ) {
dend = cdb_unpack(self->mem.buf);
curpos += bytes;
while ( curpos < dend - 8) {
bytes = PerlIO_read( self->fd, self->mem.buf, 8 );
if ( bytes != 8 )
fileerror(self, "read", self->fn);
curpos += bytes;
klen = cdb_unpack(self->mem.buf);
vlen = cdb_unpack(self->mem.buf + 4);
if (dend - klen < curpos || dend - vlen < curpos + klen)
fileerror(self, "read", self->fn);
if ( klen > 0 ) {
MAKESV( key, klen );
if ( perlio_bread( self->fd, SvPVX(key), klen ) < 0 ) {
fileerror(self, "read", self->fn);
};
curpos += klen;
PUSHWITHNULL( key, klen );
}
curpos += vlen;
PerlIO_seek(self->fd, vlen, SEEK_CUR);
};
} else {
fileerror(self, "read", self->fn);
}
/* go back to original position in file */
PerlIO_seek( self->fd, prevpos, SEEK_SET );
if ( PerlIO_error( self->fd ) )
fileerror(self, "set position", self->fn);
}
memfree( self );
}
int
put_add(self, ...)
CDB_TinyCDB *self
ALIAS:
put_replace = CDB_PUT_REPLACE
put_replace0 = CDB_PUT_REPLACE0
put_warn = CDB_PUT_WARN
INIT:
assert_status( self, METHOD_ADD );
CODE:
{
char *key, *val;
STRLEN klen, vlen;
int mode, result, i;
mode = ix ? ix : CDB_PUT_ADD;
RETVAL = 0;
for ( i = 1; i < items; i += 2 ) {
key = SvPV( ST(i), klen );
val = SvPV( ST(i+1), vlen );
result = cdb_make_put(&self->cdbm, key, klen, val, vlen, mode);
if ( result < 0 ) {
fileerror(self, "update", self->fntemp);
} else if ( result > 0 && mode == CDB_PUT_WARN) {
warn("Key %s already exists - added anyway", key);
}
if ( mode == CDB_PUT_ADD || mode == CDB_PUT_WARN) {
RETVAL++;
} else {
RETVAL += result;
};
};
}
OUTPUT:
RETVAL
int
put_insert(self, key, val)
CDB_TinyCDB *self
char *key
char *val
INIT:
assert_status( self, METHOD_INSERT );
CODE:
{
RETVAL = cdb_make_put(&self->cdbm, key, strlen(key), val, strlen(val), CDB_PUT_INSERT);
if ( RETVAL < 0 ) {
fileerror(self, "update", self->fntemp);
} else if ( RETVAL > 0) {
croak("Unable to insert new record - key exists");
} else {
RETVAL++;
}
}
OUTPUT:
RETVAL
void
finish( self, ... )
CDB_TinyCDB *self
INIT:
assert_status( self, METHOD_FINISH );
PPCODE:
{
int save_changes = 1;
int reopen = 1;
char *key;
STRLEN klen;
int i;
for ( i = 1; i < items; i += 2 ) {
key = SvPVx( ST(i), klen);
if ( strEQ(key, "save_changes") ) {
save_changes = SvTRUE(ST(i+1)) ? 1 : 0;
} else if ( strEQ(key, "reopen") ) {
reopen = SvTRUE(ST(i+1)) ? 1 : 0;
} else {
croak("Invalid option %s", key);
}
}
commit( self, save_changes, reopen );
}
void
DESTROY(self)
CDB_TinyCDB *self
PPCODE:
{
commit( self, /* save_changes */ 0, /* reopen */ 0 );
memfree( self );
Safefree( self->fn );
if ( self->fntemp ) {
Safefree( self->fntemp );
}
Safefree( self );
}