ref: 13590f759547f4ef544b251f2966b4ed2d82038c
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 (tp->key == key) return tp; 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) { Dtuple *tp, *ntp; if (!(dv && dv->tuple)) return; /* delete first tuple */ if (key == dv->tuple->key || strcmp(key, dv->tuple->key) == 0) { tp = dv->tuple; dv->tuple = tp->next; free(tp->key); if (tp->value) free(tp->value); free(tp); return; } for (tp = dv->tuple; tp->next; tp = tp->next) { if (tp->next->key == key || strcmp(tp->next->key, key) == 0) break; /* tp is parent of found tuple */ continue; } if (!tp->next) return; ntp = tp->next; tp->next = tp->next->next; free(ntp->key); if (ntp->value) free(ntp->value); free(ntp); } 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; } } }