/*
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006
* Tama Communications Corporation
*
* This file is part of GNU GLOBAL.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef STDC_HEADERS
#include
#endif
#ifdef HAVE_STRING_H
#include
#else
#include
#endif
#ifdef HAVE_UNISTD_H
#include
#endif
#include "char.h"
#include "checkalloc.h"
#include "dbop.h"
#include "die.h"
#include "locatestring.h"
#include "strbuf.h"
#include "strlimcpy.h"
#include "test.h"
int print_statistics = 0;
#define ismeta(p) (*((char *)(p)) == ' ')
/*
* dbop_open: open db database.
*
* i) path 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(const char *path, int mode, int perm, int flags)
{
DB *db;
int rw = 0;
DBOP *dbop;
BTREEINFO info;
/*
* setup arguments.
*/
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.psize = DBOP_PAGESIZE;
/*
* Decide cache size. The default value is 5MB.
* See libutil/gparam.h for the details.
*/
info.cachesize = GTAGSCACHE;
if (getenv("GTAGSCACHE") != NULL)
info.cachesize = atoi(getenv("GTAGSCACHE"));
if (info.cachesize < GTAGSMINCACHE)
info.cachesize = GTAGSMINCACHE;
/*
* if unlink do job normally, those who already open tag file can use
* it until closing.
*/
if (path != NULL && mode == 1 && test("f", path))
(void)unlink(path);
db = dbopen(path, rw, 0600, DB_BTREE, &info);
if (!db)
return NULL;
dbop = (DBOP *)check_calloc(sizeof(DBOP), 1);
if (path == NULL)
dbop->dbname[0] = '\0';
else
strlimcpy(dbop->dbname, path, sizeof(dbop->dbname));
dbop->db = db;
dbop->openflags = flags;
dbop->perm = (mode == 1) ? perm : 0;
dbop->lastdat = NULL;
dbop->lastsize = 0;
return dbop;
}
/*
* dbop_get: get data by a key.
*
* i) dbop descripter
* i) name name
* r) pointer to data
*/
const char *
dbop_get(DBOP *dbop, const char *name)
{
DB *db = dbop->db;
DBT key, dat;
int status;
key.data = (char *)name;
key.size = strlen(name)+1;
status = (*db->get)(db, &key, &dat, 0);
dbop->lastdat = (char *)dat.data;
dbop->lastsize = dat.size;
switch (status) {
case RET_SUCCESS:
break;
case RET_ERROR:
die("cannot read from database.");
case RET_SPECIAL:
return (NULL);
}
return (dat.data);
}
/*
* dbop_put: put data by a key.
*
* i) dbop descripter
* i) name key
* i) data data
*/
void
dbop_put(DBOP *dbop, const char *name, const char *data)
{
DB *db = dbop->db;
DBT key, dat;
int status;
int len;
if (!(len = strlen(name)))
die("primary key size == 0.");
if (len > 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_put_withlen: put data by a key.
*
* i) dbop descripter
* i) name key
* i) data data
* i) length length of data
*/
void
dbop_put_withlen(DBOP *dbop, const char *name, const char *data, int length)
{
DB *db = dbop->db;
DBT key, dat;
int status;
int len;
if (!(len = strlen(name)))
die("primary key size == 0.");
if (len > MAXKEYLEN)
die("primary key too long.");
key.data = (char *)name;
key.size = strlen(name)+1;
dat.data = (char *)data;
dat.size = length;
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_delete: delete record by path name.
*
* i) dbop descripter
* i) path path name
*/
void
dbop_delete(DBOP *dbop, const char *path)
{
DB *db = dbop->db;
DBT key;
int status;
if (path) {
key.data = (char *)path;
key.size = strlen(path)+1;
status = (*db->del)(db, &key, 0);
} else
status = (*db->del)(db, &key, R_CURSOR);
if (status == RET_ERROR)
die("cannot delete record.");
}
/*
* dbop_update: update record.
*
* i) dbop descripter
* i) key key
* i) dat data
*/
void
dbop_update(DBOP *dbop, const char *key, const char *dat)
{
dbop_put(dbop, key, dat);
}
/*
* dbop_first: get first record.
*
* i) dbop dbop descripter
* i) name key value or prefix
* !=NULL: indexed read by key
* ==NULL: sequential read
* i) preg compiled regular expression if any.
* 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
*/
const char *
dbop_first(DBOP *dbop, const char *name, regex_t *preg, int flags)
{
DB *db = dbop->db;
DBT key, dat;
int status;
dbop->preg = preg;
if (flags & DBOP_PREFIX && !name)
flags &= ~DBOP_PREFIX;
if (name) {
if (strlen(name) > MAXKEYLEN)
die("primary key too long.");
strlimcpy(dbop->key, name, sizeof(dbop->key));
key.data = (char *)name;
key.size = strlen(name);
/*
* includes NULL character unless prefix read.
*/
if (!(flags & DBOP_PREFIX))
key.size++;
dbop->keylen = key.size;
for (status = (*db->seq)(db, &key, &dat, R_CURSOR);
status == RET_SUCCESS;
status = (*db->seq)(db, &key, &dat, R_NEXT)) {
if (flags & DBOP_PREFIX) {
if (strncmp((char *)key.data, dbop->key, dbop->keylen))
return NULL;
} else {
if (strcmp((char *)key.data, dbop->key))
return NULL;
}
if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
continue;
break;
}
} else {
dbop->keylen = dbop->key[0] = 0;
for (status = (*db->seq)(db, &key, &dat, R_FIRST);
status == RET_SUCCESS;
status = (*db->seq)(db, &key, &dat, R_NEXT)) {
/* skip meta records */
if (ismeta(key.data))
continue;
if (preg && regexec(preg, (char *)key.data, 0, 0, 0) != 0)
continue;
break;
}
}
dbop->lastdat = (char *)dat.data;
dbop->lastsize = dat.size;
dbop->lastkey = (char *)key.data;
dbop->lastkeysize = key.size;
switch (status) {
case RET_SUCCESS:
break;
case RET_ERROR:
die("dbop_first failed.");
case RET_SPECIAL:
return (NULL);
}
dbop->ioflags = flags;
if (flags & DBOP_KEY) {
strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
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.
*/
const char *
dbop_next(DBOP *dbop)
{
DB *db = dbop->db;
int flags = dbop->ioflags;
DBT key, dat;
int status;
if (dbop->unread) {
dbop->unread = 0;
return dbop->lastdat;
}
while ((status = (*db->seq)(db, &key, &dat, R_NEXT)) == RET_SUCCESS) {
assert(dat.data != NULL);
/* skip meta records */
if (flags & DBOP_KEY && ismeta(key.data))
continue;
else if (ismeta(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.");
strlimcpy(dbop->prev, (char *)key.data, sizeof(dbop->prev));
}
dbop->lastdat = (char *)dat.data;
dbop->lastsize = dat.size;
dbop->lastkey = (char *)key.data;
dbop->lastkeysize = key.size;
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 (dbop->preg && regexec(dbop->preg, (char *)key.data, 0, 0, 0) != 0)
continue;
return (flags & DBOP_KEY) ? (char *)key.data : (char *)dat.data;
}
if (status == RET_ERROR)
die("dbop_next failed.");
return NULL;
}
/*
* dbop_unread: unread record to read again.
*
* i) dbop dbop descripter
*
* Dbop_next will read this record later.
*/
void
dbop_unread(DBOP *dbop)
{
dbop->unread = 1;
}
/*
* dbop_lastdat: get last data
*
* i) dbop dbop descripter
* r) last data
*/
const char *
dbop_lastdat(DBOP *dbop, int *size)
{
if (size)
*size = dbop->lastsize;
return dbop->lastdat;
}
/*
* get_flag: get flag value
*/
const char *
dbop_getflag(DBOP *dbop)
{
int size;
const char *dat = dbop_lastdat(dbop, &size);
const char *flag = "";
/*
* Dat format is like follows.
* dat 'xxxxxxx\0ffff\0'
* (data) (flag)
*/
if (dat) {
int i = strlen(dat) + 1;
if (size > i)
flag = dat + i;
}
return flag;
}
/*
* dbop_getoption: get option
*/
const char *
dbop_getoption(DBOP *dbop, const char *key)
{
static char buf[1024];
const char *p;
if ((p = dbop_get(dbop, key)) == NULL)
return NULL;
if (dbop->lastsize <= strlen(key))
die("illegal format (dbop_getoption).");
for (p += strlen(key); *p && isspace((unsigned char)*p); p++)
;
strlimcpy(buf, p, sizeof(buf));
return buf;
}
/*
* dbop_putoption: put option
*/
void
dbop_putoption(DBOP *dbop, const char *key, const char *string)
{
char buf[1024];
if (string)
snprintf(buf, sizeof(buf), "%s %s", key, string);
else
snprintf(buf, sizeof(buf), "%s", key);
dbop_put(dbop, key, buf);
}
/*
* dbop_getversion: get format version
*/
int
dbop_getversion(DBOP *dbop)
{
int format_version = 1; /* default format version */
const char *p;
if ((p = dbop_getoption(dbop, VERSIONKEY)) != NULL)
format_version = atoi(p);
return format_version;
}
/*
* dbop_putversion: put format version
*/
void
dbop_putversion(DBOP *dbop, int version)
{
char number[32];
snprintf(number, sizeof(number), "%d", version);
dbop_putoption(dbop, VERSIONKEY, number);
}
/*
* dbop_close: close db
*
* i) dbop dbop descripter
*/
void
dbop_close(DBOP *dbop)
{
DB *db = dbop->db;
#ifdef USE_DB185_COMPAT
(void)db->close(db);
#else
/*
* If DBOP_REMOVE is specified, omit writing to the disk in __bt_close().
*/
(void)db->close(db, (dbop->openflags & DBOP_REMOVE || dbop->dbname[0] == '\0') ? 1 : 0);
#endif
if (dbop->dbname[0] != '\0') {
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);
}