freebsd-dev/contrib/global/lib/dbop.c

333 lines
7.6 KiB
C
Raw Normal View History

/*
* Copyright (c) 1996, 1997, 1998 Shigio Yamaguchi. All rights reserved.
*
* Redilogibution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redilogibutions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redilogibutions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the dilogibution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Shigio Yamaguchi.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* dbop.c 12-Nov-98
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dbop.h"
#include "die.h"
#include "test.h"
static DBT key; /* key of record */
static DBT dat; /* data of record */
/*
* dbop_open: open db database.
*
* i) dbname database name
* i) mode 0: read only, 1: create, 2: modify
* i) perm file permission
* i) flags
* DBOP_DUP: allow duplicate records.
* DBOP_REMOVE: remove on closed.
* r) descripter for dbop_xxx()
*/
DBOP *
dbop_open(dbname, mode, perm, flags)
const char *dbname;
int mode;
int perm;
int flags;
{
DB *db;
int rw = 0;
DBOP *dbop;
BTREEINFO info;
/*
* setup argments.
*/
switch (mode) {
case 0:
rw = O_RDONLY;
break;
case 1:
rw = O_RDWR|O_CREAT|O_TRUNC;
break;
case 2:
rw = O_RDWR;
break;
default:
assert(0);
}
memset(&info, 0, sizeof(info));
if (flags & DBOP_DUP)
info.flags |= R_DUP;
info.cachesize = 500000;
/*
* if unlink do job normally, those who already open tag file can use
* it until closing.
*/
if (mode == 1 && test("f", dbname))
(void)unlink(dbname);
db = dbopen(dbname, rw, 0600, DB_BTREE, &info);
if (!db)
return NULL;
if (!(dbop = (DBOP *)malloc(sizeof(DBOP))))
die("short of memory.");
strcpy(dbop->dbname, dbname);
dbop->db = db;
dbop->openflags = flags;
dbop->perm = (mode == 1) ? perm : 0;
dbop->lastkey = NULL;
dbop->lastdat = NULL;
return dbop;
}
/*
* dbop_get: get data by a key.
*
* i) dbop descripter
* i) name name
* r) pointer to data
*/
char *
dbop_get(dbop, name)
DBOP *dbop;
const char *name;
{
DB *db = dbop->db;
int status;
key.data = (char *)name;
key.size = strlen(name)+1;
status = (*db->get)(db, &key, &dat, 0);
dbop->lastkey = (char *)key.data;
dbop->lastdat = (char *)dat.data;
switch (status) {
case RET_SUCCESS:
break;
case RET_ERROR:
die("cannot read from database.");
case RET_SPECIAL:
return (NULL);
}
return((char *)dat.data);
}
/*
* dbop_put: put data by a key.
*
* i) dbop descripter
* i) name key
* i) data data
*/
void
dbop_put(dbop, name, data)
DBOP *dbop;
const char *name;
const char *data;
{
DB *db = dbop->db;
int status;
if (strlen(name) > MAXKEYLEN)
die("primary key too long.");
key.data = (char *)name;
key.size = strlen(name)+1;
dat.data = (char *)data;
dat.size = strlen(data)+1;
status = (*db->put)(db, &key, &dat, 0);
switch (status) {
case RET_SUCCESS:
break;
case RET_ERROR:
case RET_SPECIAL:
die("cannot write to database.");
}
}
/*
* dbop_del: delete record by a key.
*
* i) dbop descripter
* i) name key
*/
void
dbop_del(dbop, name)
DBOP *dbop;
const char *name;
{
DB *db = dbop->db;
int status;
if (name) {
key.data = (char *)name;
key.size = strlen(name)+1;
status = (*db->del)(db, &key, 0);
} else
status = (*db->del)(db, &key, R_CURSOR);
if (status == RET_ERROR)
die("cannot delete record.");
}
/*
* dbop_first: get first record.
*
* i) dbop dbop descripter
* i) name key
* !=NULL: indexed read by key
* ==NULL: sequential read
* i) flags following dbop_next call take over this.
* DBOP_KEY read key part
* DBOP_PREFIX prefix read
* only valied when sequential read
* r) data
*/
char *
dbop_first(dbop, name, flags)
DBOP *dbop;
const char *name;
int flags;
{
DB *db = dbop->db;
int status;
if (flags & DBOP_PREFIX && !name)
flags &= ~DBOP_PREFIX;
if (name) {
if (strlen(name) > MAXKEYLEN)
die("primary key too long.");
strcpy(dbop->key, name);
key.data = (char *)name;
key.size = strlen(name);
/*
* includes NULL character unless prefix read.
*/
if (!(flags & DBOP_PREFIX))
key.size++;
dbop->keylen = key.size;
status = (*db->seq)(db, &key, &dat, R_CURSOR);
} else {
dbop->keylen = dbop->key[0] = 0;
/* skip META records */
for (status = (*db->seq)(db, &key, &dat, R_FIRST);
status == RET_SUCCESS;
status = (*db->seq)(db, &key, &dat, R_NEXT)) {
int c = (flags & DBOP_KEY) ? *((char *)key.data) : *((char *)dat.data);
if (c != ' ')
break;
}
}
dbop->lastkey = (char *)key.data;
dbop->lastdat = (char *)dat.data;
switch (status) {
case RET_SUCCESS:
break;
case RET_ERROR:
die("dbop_first failed.");
case RET_SPECIAL:
return (NULL);
}
dbop->ioflags = flags;
if (flags & DBOP_PREFIX) {
if (strncmp((char *)key.data, dbop->key, dbop->keylen))
return NULL;
} else if (dbop->keylen) {
if (strcmp((char *)key.data, dbop->key))
return NULL;
}
if (flags & DBOP_KEY) {
strcpy(dbop->prev, (char *)key.data);
return (char *)key.data;
}
return ((char *)dat.data);
}
/*
* dbop_next: get next record.
*
* i) dbop dbop descripter
* r) data
*
* Db_next always skip meta records.
*/
char *
dbop_next(dbop)
DBOP *dbop;
{
DB *db = dbop->db;
int flags = dbop->ioflags;
int status;
while ((status = (*db->seq)(db, &key, &dat, R_NEXT)) == RET_SUCCESS) {
assert(dat.data != NULL);
if (flags & DBOP_KEY && *((char *)key.data) == ' ')
continue;
else if (*((char *)dat.data) == ' ')
continue;
if (flags & DBOP_KEY) {
if (!strcmp(dbop->prev, (char *)key.data))
continue;
if (strlen((char *)key.data) > MAXKEYLEN)
die("primary key too long.");
strcpy(dbop->prev, (char *)key.data);
}
dbop->lastkey = (char *)key.data;
dbop->lastdat = (char *)dat.data;
if (flags & DBOP_PREFIX) {
if (strncmp((char *)key.data, dbop->key, dbop->keylen))
return NULL;
} else if (dbop->keylen) {
if (strcmp((char *)key.data, dbop->key))
return NULL;
}
return (flags & DBOP_KEY) ? (char *)key.data : (char *)dat.data;
}
if (status == RET_ERROR)
die("dbop_next failed.");
return NULL;
}
/*
* dbop_close: close db
*
* i) dbop dbop descripter
*/
void
dbop_close(dbop)
DBOP *dbop;
{
DB *db = dbop->db;
(void)db->close(db);
if (dbop->openflags & DBOP_REMOVE)
(void)unlink(dbop->dbname);
else if (dbop->perm && chmod(dbop->dbname, dbop->perm) < 0)
die("cannot change file mode.");
(void)free(dbop);
}