ref: 2f61e9979e69ab014cdd44ea71fa787ea3454446
dir: /db.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include "db.h"
#define DEBUG
#undef DEBUG
#define LOG 1
#define VERBOSE 2
#define VERYVERBOSE 3
static int verbosity = LOG;
static int consfd = -1; /* 2 for stderr, -1 for cons */
static void
clog(int v, char *fmt, ...)
{
#ifdef DEBUG
va_list args;
if (v > verbosity)
return;
if (consfd < 0) {
consfd = open("#c/cons", OWRITE|OCEXEC);
if (consfd < 0)
return;
}
fprint(consfd, "bliedb: ");
va_start(args, fmt);
vfprint(consfd, fmt, args);
va_end(args);
fprint(consfd, "\n");
#else
USED(v, fmt);
#endif
}
static char*
estrdup(char *s)
{
char *t;
t = strdup(s);
if (!t)
sysfatal("out of memory: %r");
return t;
}
static char*
unesc(char *s)
{
char *t;
int n;
n = strlen(s);
while (t = strstr(s, "''")) {
memmove(t, t+1, n - (t-s));
n--;
}
clog(VERYVERBOSE, "parse: unesc: %s", s);
return s;
}
enum {
OUT = 0,
QUOT = 1,
};
static Dpack*
pgetdv(Db *db, char *i)
{
clog(VERBOSE, "parse: dpack %s", i);
return getdpack(db, i);
}
static void
paddkv(Dpack *dv, char *k, char *v)
{
clog(VERBOSE, "parse: %s = %s", k, v);
setdval(dv, k, unesc(v));
}
char *allowed = ".(){}!#$%&*+,-/:;<>?@[]^\_`|~\"";
static int
iswc(int c)
{
return isalnum(c) || strchr(allowed, c);
}
static void
parsedb(Db *db, char *s)
{
Dpack *dv;
char *k, *v;
int state = 0;
char *t;
int doquit;
clog(LOG, "parse: begin parsing: %s", s);
dv = nil;
t = nil;
k = nil;
v = nil;
doquit = 0;
while (1) {
if (doquit)
break;
clog(VERYVERBOSE, "parse: state: %d, char '%c'", state, *s);
if (state == QUOT && *s == 0) {
clog(LOG, "parse: bad syntax EOL");
break;
}
if (state == OUT && (*s == ' ' || *s == '\t' || *s == 0)) {
if (*s == 0) {
clog(VERBOSE, "parse: regular EOL");
doquit++;
}
if (!t) {
s++;
continue;
}
if (!dv) {
*s = 0;
dv = pgetdv(db, t);
t = nil;
s++;
continue;
}
if (!k) {
*s = 0;
k = t;
clog(VERBOSE, "parse: key %s", k);
t = nil;
s++;
continue;
}
if (!v) {
*s = 0;
v = t;
paddkv(dv, k, v);
k = nil;
v = nil;
t = nil;
s++;
continue;
}
goto Bad;
}
if (state == QUOT && (*s == ' ' || *s == '\t' || *s == '=')) {
goto Alnum;
}
if (state == OUT && *s == '\'') {
state = QUOT;
clog(VERYVERBOSE, "parse: {quote");
s++;
t = s;
continue;
}
if (state == QUOT && *s == '\'') {
if (s[1] == '\'') {
s++; /* skip escaped quote */
clog(VERYVERBOSE, "parse: esc: >%c<", *s);
goto Alnum;
}
state = OUT;
clog(VERYVERBOSE, "parse: }quote");
*s = 0;
s++;
continue;
}
if (iswc(*s)) { /* both states */
Alnum:
if (!t) {
clog(VERYVERBOSE, "parse: encountered new word");
t = s;
}
s++;
continue;
}
if (state == OUT && *s == '=') {
*s = ' ';
continue;
}
Bad:
clog(LOG, "parse: bad state: got '%c'", *s);
break;
}
clog(LOG, "parse: finished parsing line");
}
Db*
opendb(char *file)
{
Biobuf *bin;
int fd;
char *s;
Db *db;
/* try to install "%q" quote fmt, will silently
* fail if it's already installed.
*/
quotefmtinstall();
if (!file) {
db = mallocz(sizeof(Db), 1);
return db;
}
fd = open(file, OREAD);
if (fd < 0) {
db = mallocz(sizeof(Db), 1);
db->file = estrdup(file);
return db;
}
bin = Bfdopen(fd, OREAD);
if (!bin)
sysfatal("%r");
db = mallocz(sizeof(Db), 1);
db->file = estrdup(file);
while (s = Brdstr(bin, '\n', 1)) {
parsedb(db, s);
free(s);
}
Bterm(bin);
return db;
}
static void
freedpack(Dpack *dv)
{
Dtuple *tp, *ttp;
for (tp = dv->tuple; tp;) {
free(tp->key);
if (tp->value)
free(tp->value);
ttp = tp->next;
free(tp);
tp = ttp;
}
free(dv->id);
}
void
freedb(Db *db)
{
Dpack *dv, *tdv;
for (dv = db->dpack; dv;) {
tdv = dv->next;
freedpack(dv);
dv = tdv;
}
if (db->file)
free(db->file);
free(db);
}
int
writedb(Db *db, char *file)
{
int fd;
Biobuf *bout;
Dpack *dv;
Dtuple *tp;
if (!file)
file = db->file;
if (!file) {
werrstr("no file");
return 0;
}
fd = create(file, OWRITE|OTRUNC, 0666);
if (fd < 0) {
werrstr("create: %r");
return 0;
}
bout = Bfdopen(fd, OWRITE);
if (!bout)
sysfatal("%r");
// TODO: implement esc sequence
for (dv = db->dpack; dv; dv = dv->next) {
Bprint(bout, "%s", dv->id);
for (tp = dv->tuple; tp; tp = tp->next) {
if (tp->value) {
Bprint(bout, " %s=%q", tp->key, tp->value);
} else {
Bprint(bout, " %s", tp->key);
}
}
Bprint(bout, "\n");
}
Bterm(bout);
return 1;
}
Dpack*
getdpack(Db *db, char *id)
{
Dpack *dv, *pdv;
if (!db->dpack) {
db->dpack = mallocz(sizeof(Dpack), 1);
db->dpack->id = estrdup(id);
return db->dpack;
}
pdv = nil; /* shut up compiler */
for (dv = db->dpack; dv; dv = dv->next) {
if (strcmp(dv->id, id) == 0)
return dv;
pdv = dv;
}
pdv->next = mallocz(sizeof(Dpack), 1);
dv = pdv->next;
dv->id = estrdup(id);
return dv;
}
static Dtuple*
getdtuple(Dpack *dv, char *key)
{
Dtuple *tp, *ptp;
if (!dv->tuple) {
dv->tuple = mallocz(sizeof(Dtuple), 1);
dv->tuple->key = estrdup(key);
return dv->tuple;
}
ptp = nil; /* shut up compiler */
for (tp = dv->tuple; tp; tp = tp->next) {
if (strcmp(tp->key, key) == 0)
return tp;
ptp = tp;
}
ptp->next = mallocz(sizeof(Dtuple), 1);
tp = ptp->next;
tp->key = estrdup(key);
return tp;
}
void
setdval(Dpack *dv, char *key, char *value)
{
Dtuple *dt;
dt = getdtuple(dv, key);
if (dt->value)
free(dt->value);
dt->value = value ? estrdup(value) : nil;
}
char*
getdval(Dpack *dv, char *key, char *def)
{
Dtuple *tp;
for (tp = dv->tuple; tp; tp = tp->next) {
if (strcmp(tp->key, key) == 0)
return tp->value;
}
return def;
}
void
deldtuple(Dpack *dv, char *key)
{
USED(dv, key);
sysfatal("deldtuple not implemented yet!");
}
void
deldpack(Db *db, Dpack *dv)
{
USED(db, dv);
sysfatal("deldpack not implemented yet!");
}
int
typelen(Stype t)
{
switch (t) {
case INT:
return sizeof(int);
case FLOAT:
return sizeof(double);
case STRING:
return sizeof(char*);
}
return 0;
}
void
serialize(Dpack *dv, void *data, Sdata *desc)
{
char buf[32], *str;
Sdata *s;
uchar *p;
int *ip;
double *fp;
char **sp;
p = (uchar*)data;
for (s = desc; s->type; s++) {
str = nil;
switch (s->type) {
case NIL:
return;
case INT:
ip = (int*)p;
snprint(buf, sizeof buf, "%d", *ip);
str = buf;
p += typelen(INT);
break;
case STRING:
sp = (char**)p;
str = *sp;
p += typelen(STRING);
break;
case FLOAT:
fp = (double*)p;
snprint(buf, sizeof buf, "%f", *fp);
str = buf;
p += typelen(FLOAT);
break;
}
setdval(dv, s->name, str);
}
}
void
deserialize(Dpack *dv, void *data, Sdata *desc)
{
char *str;
Sdata *s;
uchar *p;
int *ip;
double *fp;
char **cp;
p = (uchar*)data;
for (s = desc; s->type; s++) {
clog(VERBOSE, "des: '%s' (%d)", s->name, s->type);
str = getdval(dv, s->name, s->def);
if (!str) {
clog(LOG, "des: %s not found", s->name);
clog(VERYVERBOSE, "des: ptr: %p", p);
p += typelen(s->type);
continue;
}
switch (s->type) {
case NIL:
return;
case INT:
ip = (int*)p;
*ip = atoi(str);
clog(VERYVERBOSE, "des: int: %d", *ip);
p += typelen(INT);
continue;
case STRING:
cp = (char**)p;
*cp = str;
clog(VERYVERBOSE, "des: str: %s", *cp);
p += typelen(STRING);
continue;
case FLOAT:
fp = (double*)p;
*fp = atof(str);
clog(VERYVERBOSE, "des: float: %f", *fp);
p += typelen(FLOAT);
continue;
}
}
}