1
/* $Id: dbdb.c,v 1.22 2002/10/19 09:59:35 tommy Exp $ */
4
* Copyright (c) 2002 Tom Marshall <tommy@tig-grr.com>
6
* This program is free software. It may be distributed under the terms
7
* in the file LICENSE, found in the top level of the distribution.
9
* dbdb.c: berkeley database handler
23
#define DBT_init( pdbt ) memset( pdbt, 0, sizeof(DBT) )
25
#if !defined(DB_VERSION_MAJOR) /* v1 */
26
#define dbx_get(dbp,kp,vp) dbp->get( dbp, kp, vp, 0 )
27
#define dbx_put(dbp,kp,vp) dbp->put( dbp, kp, vp, 0 )
28
#define dbx_fd(dbp,fd) fd = dbp->fd( dbp )
30
#define dbx_get(dbp,kp,vp) dbp->get( dbp, NULL, kp, vp, 0 )
31
#define dbx_put(dbp,kp,vp) dbp->put( dbp, NULL, kp, vp, 0 )
32
#define dbx_fd(dbp,fd) dbp->fd( dbp, &fd )
33
#endif /* DB_VERSION_MAJOR */
35
#if !defined(DB_VERSION_MAJOR) /* v1 */
36
typedef DB DBC; /* no separate cursor type */
37
#define dbx_createcursor(dbp,dbcp) ((dbcp = dbp) ? 0 : -1)
38
#define dbx_destroycursor(dbcp) (dbcp = NULL)
39
#define dbx_first(dbcp,kp,vp) dbcp->seq(dbcp,kp,vp,R_FIRST)
40
#define dbx_next(dbcp,kp,vp) dbcp->seq(dbcp,kp,vp,R_NEXT)
41
#define dbx_prev(dbcp,kp,vp) dbcp->seq(dbcp,kp,vp,R_PREV)
42
#define dbx_last(dbcp,kp,vp) dbcp->seq(dbcp,kp,vp,R_LAST)
43
#elif DB_VERSION_MAJOR == 2
44
#define dbx_createcursor(dbp,dbcp) dbp->cursor(dbp,NULL,&csrp)
45
#define dbx_destroycursor(dbcp) dbcp->c_close(dbcp)
46
#define dbx_first(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_FIRST)
47
#define dbx_next(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_NEXT)
48
#define dbx_prev(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_PREV)
49
#define dbx_last(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_LAST)
51
#define dbx_createcursor(dbp,dbcp) dbp->cursor(dbp,NULL,&csrp,0)
52
#define dbx_destroycursor(dbcp) dbcp->c_close(dbcp)
53
#define dbx_first(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_FIRST)
54
#define dbx_next(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_NEXT)
55
#define dbx_prev(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_PREV)
56
#define dbx_last(dbcp,kp,vp) dbcp->c_get(dbcp,kp,vp,DB_LAST)
57
#endif /* DB_VERSION_MAJOR */
59
static void char2DBT( DBT* pdbt, char* p )
62
pdbt->size = strlen(p);
65
static void uint2DBT( DBT* pdbt, uint* p )
68
pdbt->size = sizeof(uint);
71
static uint DBT2uint( DBT* pdbt )
74
memcpy( &n, pdbt->data, sizeof(n) );
78
dbh_t* dbdb_db_open( cpchar dbhost, cpchar dbname, cpchar dbuser, cpchar dbpass )
86
pthis = (dbhdb_t*)malloc( sizeof(dbhdb_t) );
91
pthis->close = dbdb_db_close;
92
pthis->opentable = dbdb_db_opentable;
93
if( dbname != NULL && *dbname != '\0' )
95
dirlen = strlen( dbname );
96
pthis->dir = strdup( dbname );
97
if( pthis->dir[dirlen-1] == '/' )
99
pthis->dir[dirlen-1] = '\0';
104
phome = getenv( "HOME" );
105
if( phome == NULL || *phome == '\0' )
109
pthis->dir = (char*)malloc( strlen(phome)+5+1 );
110
if( pthis->dir == NULL )
114
sprintf( pthis->dir, "%s/.bmf", phome );
117
/* ensure config directory exists */
118
if( stat( pthis->dir, &st ) != 0 )
120
if( errno == ENOENT )
122
if( mkdir( pthis->dir, S_IRUSR|S_IWUSR|S_IXUSR ) != 0 )
134
if( !S_ISDIR( st.st_mode ) )
140
#if !defined(DB_VERSION_MAJOR) || DB_VERSION_MAJOR < 3
141
/* no initialization */
142
#else /* DB_VERSION_MAJOR >= 3 */
143
if( db_env_create( &pthis->envp, 0 ) != 0 )
147
if( pthis->envp->open( pthis->envp, pthis->dir, DB_INIT_LOCK|DB_INIT_MPOOL|DB_CREATE, 0644 ) != 0 )
151
#endif /* DB_VERSION_MAJOR */
153
return (dbh_t*)pthis;
160
bool_t dbdb_db_close( dbhdb_t* pthis )
162
#if !defined(DB_VERSION_MAJOR) || DB_VERSION_MAJOR < 3
164
#else /* DB_VERSION_MAJOR >= 3 */
165
pthis->envp->close( pthis->envp, 0 );
166
#endif /* DB_VERSION_MAJOR */
174
dbt_t* dbdb_db_opentable( dbhdb_t* pthis, cpchar table, bool_t rdonly )
181
char szpath[PATH_MAX];
183
ptable = (dbtdb_t*)malloc( sizeof(dbtdb_t) );
188
ptable->close = dbdb_table_close;
189
ptable->mergeclose = dbdb_table_mergeclose;
190
ptable->unmergeclose = dbdb_table_unmergeclose;
191
ptable->import = dbdb_table_import;
192
ptable->export = dbdb_table_export;
193
ptable->getmsgcount = dbdb_table_getmsgcount;
194
ptable->getcount = dbdb_table_getcount;
197
sprintf( szpath, "%s/%s.db", pthis->dir, table );
198
#if !defined(DB_VERSION_MAJOR)
199
if( (dbp = dbopen( szpath, O_CREAT|O_RDWR, 0644, DB_BTREE, NULL)) == NULL )
203
#elif DB_VERSION_MAJOR == 2
204
if( db_open( szpath, DB_BTREE, DB_CREATE, 0644, NULL, NULL, &dbp ) != 0 )
208
#elif (DB_VERSION_MAJOR == 3) || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0)
209
ptable->envp = pthis->envp;
210
if( db_create( &dbp, NULL, 0 ) != 0 )
214
if( dbp->open( dbp, szpath, NULL, DB_BTREE, DB_CREATE, 0644 ) != 0 )
219
ptable->envp = pthis->envp;
220
if( db_create( &dbp, NULL, 0 ) != 0 )
224
if( dbp->open( dbp, NULL, szpath, NULL, DB_BTREE, DB_CREATE, 0644 ) != 0 )
228
#endif /* DB_VERSION_MAJOR */
234
char2DBT( &key, MSGCOUNT_KEY );
235
if( dbx_get( dbp, &key, &val ) == 0 )
237
ptable->nmsgs = DBT2uint( &val );
240
return (dbt_t*)ptable;
247
static bool_t dbdb_table_lock( dbtdb_t* pthis )
253
dbx_fd( pthis->dbp, fd );
254
memset( &lock, 0, sizeof(lock) );
255
lock.l_type = F_WRLCK;
257
lock.l_whence = SEEK_SET;
259
if( fcntl( fd, F_SETLKW, &lock ) != 0 )
263
#endif /* ndef NOLOCK */
267
static bool_t dbdb_table_unlock( dbtdb_t* pthis )
273
dbx_fd( pthis->dbp, fd );
274
memset( &lock, 0, sizeof(lock) );
275
lock.l_type = F_UNLCK;
277
lock.l_whence = SEEK_SET;
279
if( fcntl( fd, F_SETLK, &lock ) != 0 )
283
#endif /* ndef NOLOCK */
287
bool_t dbdb_table_close( dbtdb_t* pthis )
289
DB* dbp = pthis->dbp;
293
#if !defined(DB_VERSION_MAJOR) /* v1 */
296
dbp->close( dbp, 0 );
297
#endif /* DB_VERSION_MAJOR */
304
bool_t dbdb_table_mergeclose( dbtdb_t* pthis, vec_t* pmsg )
306
DB* dbp = pthis->dbp;
310
char szword[MAXWORDLEN+1];
315
if( pthis->dbp == NULL )
320
if( !dbdb_table_lock( pthis ) )
330
char2DBT( &key, MSGCOUNT_KEY );
331
uint2DBT( &val, &pthis->nmsgs );
332
dbx_put( dbp, &key, &val );
334
vec_first( pmsg, &msgiter );
335
pmsgstr = veciter_get( &msgiter );
337
while( pmsgstr != NULL )
339
assert( pmsgstr->len <= MAXWORDLEN );
340
strncpylwr( szword, pmsgstr->p, pmsgstr->len );
341
szword[pmsgstr->len] = '\0';
342
count = db_getnewcount( &msgiter );
344
char2DBT( &key, szword );
345
if( dbx_get( dbp, &key, &val ) == 0 )
347
count += DBT2uint( &val );
349
char2DBT( &key, szword );
350
uint2DBT( &val, &count );
351
if( dbx_put( dbp, &key, &val ) != 0 )
356
veciter_next( &msgiter );
357
pmsgstr = veciter_get( &msgiter );
360
veciter_destroy( &msgiter );
361
dbdb_table_unlock( pthis );
362
return dbdb_table_close( pthis );
368
bool_t dbdb_table_unmergeclose( dbtdb_t* pthis, vec_t* pmsg )
370
DB* dbp = pthis->dbp;
374
char szword[MAXWORDLEN+1];
379
if( pthis->dbp == NULL )
384
if( pthis->nmsgs > 0 )
389
if( !dbdb_table_lock( pthis ) )
397
char2DBT( &key, MSGCOUNT_KEY );
398
uint2DBT( &val, &pthis->nmsgs );
399
dbx_put( dbp, &key, &val );
401
vec_first( pmsg, &msgiter );
402
pmsgstr = veciter_get( &msgiter );
404
while( pmsgstr != NULL )
406
assert( pmsgstr->len <= MAXWORDLEN );
407
strncpylwr( szword, pmsgstr->p, pmsgstr->len );
408
szword[pmsgstr->len] = '\0';
409
count = db_getnewcount( &msgiter );
411
char2DBT( &key, szword );
412
if( dbx_get( dbp, &key, &val ) == 0 )
414
uint n = DBT2uint( &val );
415
n = (n > count) ? (n - count) : 0;
416
char2DBT( &key, szword );
417
uint2DBT( &val, &n );
418
if( dbx_put( dbp, &key, &val ) != 0 )
424
veciter_next( &msgiter );
425
pmsgstr = veciter_get( &msgiter );
428
veciter_destroy( &msgiter );
429
dbdb_table_unlock( pthis );
430
return dbdb_table_close( pthis );
436
bool_t dbdb_table_import( dbtdb_t* pthis, cpchar filename )
438
DB* dbp = pthis->dbp;
447
char szword[MAXWORDLEN+1];
449
if( pthis->dbp == NULL )
453
if( (fd = open( filename, O_RDONLY, 0644 )) < 0 )
457
if( fstat( fd, &st ) != 0 )
461
if( st.st_size == 0 )
465
pbuf = (char*)malloc( st.st_size );
470
if( read( fd, pbuf, st.st_size ) != st.st_size )
478
if( sscanf( pbuf, BOGOFILTER_HEADER, &pthis->nmsgs ) != 1 )
483
while( *pbegin != '\n' ) pbegin++;
486
char2DBT( &key, MSGCOUNT_KEY );
487
uint2DBT( &val, &pthis->nmsgs );
488
if( dbx_put( dbp, &key, &val ) != 0 )
493
while( pbegin < pbuf + st.st_size )
500
while( *pend != '\n' )
502
if( pend >= pbuf + st.st_size )
506
*pend = tolower(*pend);
509
r.w.len = (pend-pbegin);
510
r.n = strtol( pend+1, NULL, 10 );
514
if( pend > pbegin && *pbegin != '#' && *pbegin != ';' )
516
if( r.w.len == 0 || r.w.len > MAXWORDLEN )
518
fprintf( stderr, "dbh_loadfile: bad file format\n" );
521
strncpylwr( szword, r.w.p, r.w.len );
522
szword[r.w.len] = '\0';
523
char2DBT( &key, szword );
524
uint2DBT( &val, &r.n );
525
if( dbx_put( dbp, &key, &val ) != 0 )
542
bool_t dbdb_table_export( dbtdb_t* pthis, cpchar filename )
544
DB* dbp = pthis->dbp;
546
char iobuf[IOBUFSIZE];
554
if( (fd = open( filename, O_CREAT|O_WRONLY|O_TRUNC, 0644 )) < 0 )
558
if( dbx_createcursor( dbp, csrp ) != 0 )
567
p += sprintf( p, BOGOFILTER_HEADER, pthis->nmsgs );
569
rc = dbx_first( csrp, &key, &val );
572
assert( key.data != NULL && key.size <= MAXWORDLEN );
573
assert( val.data != NULL && val.size == sizeof(uint) );
574
if( key.size != MSGCOUNT_KEY_LEN ||
575
memcmp( key.data, MSGCOUNT_KEY, MSGCOUNT_KEY_LEN ) != 0 )
577
memcpy( p, key.data, key.size ); p += key.size;
579
p += sprintf( p, "%u\n", DBT2uint(&val) );
580
if( p+TEXTDB_MAXLINELEN > (iobuf+1) )
582
write( fd, iobuf, p-iobuf );
586
rc = dbx_next( csrp, &key, &val );
588
dbx_destroycursor( csrp );
591
write( fd, iobuf, p-iobuf );
600
uint dbdb_table_getmsgcount( dbtdb_t* pthis )
605
uint dbdb_table_getcount( dbtdb_t* pthis, str_t* pword )
607
DB* dbp = pthis->dbp;
611
char szword[MAXWORDLEN+1];
614
assert( pword->len <= MAXWORDLEN );
615
strncpylwr( szword, pword->p, pword->len );
616
szword[pword->len] = '\0';
622
char2DBT( &key, szword );
623
if( dbx_get( dbp, &key, &val ) == 0 )
625
count = DBT2uint( &val );
631
#else /* def HAVE_LIBDB */
633
dbh_t* dbdb_db_open( cpchar dbhost, cpchar dbname, cpchar dbuser, cpchar dbpass )
638
#endif /* def HAVE_LIBDB */
641
int main( int argc, char** argv )
650
fprintf( stderr, "usage: %s <file>\n", argv[0] );
654
for( n = 0; n < 100; n++ )
656
pdb = dbh_open( "testlist", true );
658
vec_first( &db, &iter );
659
while( (pstr = veciter_get( &iter )) != NULL )
661
char buf[MAXWORDLEN+32];
663
if( pstr->len > 200 )
665
fprintf( stderr, "str too long: %u chars\n", pstr->len );
669
strcpy( buf, "str: " );
671
memcpy( p, pstr->p, pstr->len );
673
sprintf( p, " %u", pstr->count );
676
veciter_next( &iter );
684
#endif /* def UNIT_TEST */