shithub: blie

ref: 2f61e9979e69ab014cdd44ea71fa787ea3454446
dir: /db.c/

View raw version
#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;
		}
	}
}