#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include <ctype.h>
#include "object.h"
#include "catset.h"
#include "parse.h"
#define MAXTOKEN 1024
Biobuf *f;
static int str;
char *file;
Token tokenlistinit[] = {
{ "category", Obj, Category , "music" , {nil,0}},
{ "cddata", Obj, Cddata , nil , {nil,0}},
{ "command", Obj, Cmd , nil , {nil,0}},
{ "file", Obj, File , "file" , {nil,0}},
{ "include", Obj, Include , nil , {nil,0}},
{ "key", Obj, Key , nil , {nil,0}},
{ "lyrics", Obj, Lyrics , "lyrics" , {nil,0}},
{ "part", Obj, Part , "title" , {nil,0}},
{ "path", Obj, Path , nil , {nil,0}},
{ "performance",Obj, Performance , "artist" , {nil,0}},
{ "recording", Obj, Recording , "title" , {nil,0}},
{ "root", Obj, Root , nil , {nil,0}},
{ "search", Obj, Search , nil , {nil,0}},
{ "soloists", Obj, Soloists , "artist" , {nil,0}},
{ "time", Obj, Time , "time" , {nil,0}},
{ "track", Obj, Track , "title" , {nil,0}},
{ "work", Obj, Work , "title" , {nil,0}},
};
Token *tokenlist;
int ntoken = nelem(tokenlistinit);
int catnr = 0;
Cmdlist cmdlist[] = {
{ Sort, "sort" },
{ Enum, "number" },
{ 0x00, 0 },
};
static char *curtext;
void
inittokenlist(void)
{
int i;
ntoken = nelem(tokenlistinit);
tokenlist = malloc(sizeof(tokenlistinit));
memmove(tokenlist, tokenlistinit, sizeof(tokenlistinit));
for(i = 0; i< ntoken; i++){
tokenlist[i].name = strdup(tokenlist[i].name);
catsetinit(&tokenlist[i].categories, tokenlist[i].value);
}
curtext = smprint("{");
}
Type
gettoken(char *token)
{
char *p, *q;
int i, n;
Token *t;
for(;;){
if(curtext){
p = &curtext[strspn(curtext, " \t")];
if(*p && *p != '\n')
break;
}
do {
str++;
free(curtext);
if((curtext = Brdstr(f, '\n', 0)) == nil)
return Eof;
} while(curtext[0] == '#');
}
if(*p == '{'){
*token++ = *p;
*token = 0;
*p = ' ';
return BraceO;
}
if(*p == '}'){
*token++ = *p;
*token = 0;
*p = ' ';
return BraceC;
}
if(*p == '='){
*token++ = *p;
*token = 0;
*p = ' ';
return Equals;
}
t = nil;
n = 0;
for(i = 0; i < ntoken; i++){
t = &tokenlist[i];
if(strncmp(p, t->name, n=strlen(t->name)) == 0){
q = &p[n];
if(isalnum(*q) || *q == '-') continue;
q += strspn(q, " \t");
if(t->kind == Obj && *q == '{')
break;
if(t->kind == Cat && *q == '=')
break;
}
}
if(i < ntoken){
strcpy(token, t->name);
memset(p, ' ', n);
return i;
}
assert(strlen(token) < MAXTOKEN);
if(strchr(p, '{'))
sysfatal("Illegal keyword or parse error: %s", p);
if((q = strchr(p, '='))){
if(q == p) goto tx;
*q = 0;
strcpy(token, p);
assert(strlen(token) < MAXTOKEN);
memset(p, ' ', q-p);
*q = '=';
for(q = token; *q; q++)
if(!isalnum(*q) && !isspace(*q)) break;
if(*q) return Txt;
while(isspace(*--q)) *q = 0;
return Newcat;
}
tx: if((q = strchr(p, '}'))){
*q = 0;
strcpy(token, p);
assert(strlen(token) < MAXTOKEN);
memset(p, ' ', q-p);
*q = '}';
return Txt;
}
strcpy(token, p);
assert(strlen(token) < MAXTOKEN);
free(curtext);
curtext = nil;
return Txt;
}
Object *
getobject(Type t, Object *parent)
{
char *token;
char *textbuf;
char *tp, *p, *q;
int i;
Object *o, *oo, *child;
Token *ot;
Type nt;
token = malloc(MAXTOKEN);
textbuf = malloc(8192);
tp = textbuf;
o = newobject(t, parent);
o->flags |= Hier;
if(parent == nil){
root = o;
o->path = strdup(startdir);
setmalloctag(o->path, 0x100001);
}
if(gettoken(token) != BraceO)
sysfatal("Parse error: no brace, str %d", str);
for(;;){
t = gettoken(token);
if(t >= 0)
switch(tokenlist[t].kind){
case Obj:
switch(t){
case Key:
case Cmd:
case Path:
if(getobject(t, o) != nil)
sysfatal("Non-null child?");
break;
case Include:
case Category:
child = getobject(t, o);
if(child) addchild(o, child, "case Category");
break;
default:
/* subobject */
child = getobject(t, o);
if(child == nil)
sysfatal("Null child?");
addchild(o, child, "default");
break;
}
break;
case Cat:
catcase: nt = gettoken(token);
if(nt != Equals)
sysfatal("Expected Equals, not %s", token);
nt = gettoken(token);
if(nt != Txt)
sysfatal("Expected Text, not %s", token);
if((p = strchr(token, '\n'))) *p = 0;
p = token;
if(o->type == Category){
if(catsetisset(&o->categories)){
fprint(2, "Category object must have one category\n");
}
catsetcopy(&o->categories, &tokenlist[t].categories);
strncpy(o->key, p, KEYLEN);
if(catobjects[t] == 0)
sysfatal("Class %s not yet defined", tokenlist[t].name);
for(i = 0; i < catobjects[t]->nchildren; i++)
if(strcmp(catobjects[t]->children[i]->key, p) == 0)
break;
if(i == catobjects[t]->nchildren){
/* It's a new key for the category */
addchild(catobjects[t], o, "new key for cat");
}else{
/* Key already existed */
oo = catobjects[t]->children[i];
if(oo->value)
sysfatal("Duplicate category object for %s", oo->value);
catobjects[t]->children[i] = o;
if(oo->nchildren){
for(i = 0; i < oo->nchildren; i++){
if(oo->children[i]->parent == oo)
oo->children[i]->parent = o;
addchild(o, oo->children[i], "key already existed");
}
}
freeobject(oo, "a");
}
o->parent = catobjects[t];
}else{
catsetorset(&o->categories, &tokenlist[t].categories);
for(i = 0; i < catobjects[t]->nchildren; i++)
if(strcmp(catobjects[t]->children[i]->key, p) == 0)
break;
if(i == catobjects[t]->nchildren){
oo = newobject(Category, catobjects[t]);
/*
oo->value = strdup(token);
*/
strncpy(oo->key, p, KEYLEN);
catsetcopy(&oo->categories, &tokenlist[t].categories);
addchild(catobjects[t], oo, "catobjects[t],oo");
}
addchild(catobjects[t]->children[i], o, "children[i]");
}
break;
}
else
switch(t){
case Eof:
if(o->type == Root){
free(token);
free(textbuf);
return o;
}
sysfatal("Unexpected Eof in %s, file %s", tokenlist[o->type].name, file);
case Newcat:
/* New category, make an entry in the tokenlist */
tokenlist = realloc(tokenlist, (ntoken+1)*sizeof(Token));
ot = &tokenlist[ntoken];
ot->name = strdup(token);
setmalloctag(ot->name, 0x100002);
ot->kind = Cat;
ot->value = -1;
memset(&ot->categories, 0, sizeof(Catset));
catsetinit(&ot->categories, catnr++);
/* And make an entry in the catobjects table */
if(ncat <= ntoken){
catobjects = realloc(catobjects, (ntoken+1)*sizeof(Object*));
while(ncat <= ntoken) catobjects[ncat++] = nil;
}
if(catobjects[ntoken] != nil)
sysfatal("Class %s already defined in %s:%d", token, file, str);
if(0) fprint(2, "newcat: token %s catnr %d ntoken %d ncat %d\n",
token, catnr, ntoken, ncat);
catobjects[ntoken] = newobject(Category, root);
if(o->type == Category)
catobjects[ntoken]->flags = o->flags&Hier;
catobjects[ntoken]->flags |= Sort;
strncpy(catobjects[ntoken]->key, token, KEYLEN);
catsetcopy(&catobjects[ntoken]->categories, &ot->categories);
addchild(root, catobjects[ntoken], "root");
t = ntoken;
ntoken++;
goto catcase;
case Txt:
strcpy(tp, token);
tp += strlen(token);
break;
case BraceC:
while(tp > textbuf && tp[-1] == '\n') *--tp = 0;
if((o->type == File || o->type == Include) && o->path){
o->value = smprint("%s/%s", o->path, textbuf);
}else if(tp > textbuf){
o->value = strdup(textbuf);
setmalloctag(o->value, 0x100003);
}
switch(o->type){
case Cmd:
q = strtok(o->value, " \t,;\n");
while(q){
if(*q) for(i = 0; cmdlist[i].name; i++){
if(strcmp(q, cmdlist[i].name) == 0){
o->parent->flags |= cmdlist[i].flag;
break;
}
if(cmdlist[i].name == 0)
fprint(2, "Unknown command: %s\n", q);
}
q = strtok(nil, " \t,;\n");
}
freeobject(o, "b");
free(token);
free(textbuf);
return nil;
case Path:
p = o->value;
free(o->parent->path);
if(p[0] == '/' || o->path == nil){
o->parent->path = strdup(p);
setmalloctag(o->parent->path, 0x100004);
}else{
o->parent->path = smprint("%s/%s", o->path, p);
setmalloctag(o->parent->path, 0x100005);
}
freeobject(o, "b");
free(token);
free(textbuf);
return nil;
case Include:
free(token);
free(textbuf);
return getinclude(o);
case Category:
/*
if(o->nchildren) break;
*/
free(token);
free(textbuf);
return nil;
case Key:
strncpy(o->parent->key, o->value, KEYLEN);
freeobject(o, "d");
free(token);
free(textbuf);
return nil;
default:
break;
}
free(token);
free(textbuf);
return o;
default:
fprint(2, "Unexpected token: %s\n", token);
free(token);
free(textbuf);
return nil;
}
}
}
Object *
getinclude(Object *o)
{
char *savetext;
Biobuf *savef = f;
char *savefile, fname[256];
Object *oo;
int savestr = str;
char token[MAXTOKEN], *dirname, *filename;
Type t;
str = 0;
if(curtext){
savetext = strdup(curtext);
setmalloctag(savetext, 0x100006);
}else
savetext = nil;
if((f = Bopen(o->value, OREAD)) == nil)
sysfatal("getinclude: %s: %r", o->value);
savefile = file;
file = strdup(o->value);
strncpy(fname, o->value, 256);
if((filename = strrchr(fname, '/'))){
*filename = 0;
dirname = fname;
filename++;
}else{
dirname = "";
filename = fname;
}
while((t = gettoken(token)) != Eof){
if(t < 0){
if(*dirname)
sysfatal("Bad include file %s/%s, token %s, str %d",
dirname, filename, token, str);
else
sysfatal("Bad include file %s, token %s, str %d",
filename, token, str);
}
free(o->path);
o->path = strdup(dirname);
setmalloctag(o->path, 0x100007);
oo = getobject(t, o->parent);
if(oo) addchild(o->parent, oo, "o->parent, oo");
}
freeobject(o, "e");
free(curtext);
curtext = nil;
if(savetext)
curtext = savetext;
free(file);
file = savefile;
str = savestr;
Bterm(f);
f = savef;
return nil;
}
void
addchild(Object *parent, Object *child, char *where)
{
int i;
/* First check if child's already been added
* This saves checking elsewhere
*/
for(i = 0; i < parent->nchildren; i++)
if(parent->children[i] == child) return;
parent->children = realloc(parent->children, (i+1)*sizeof child);
parent->children[i] = child;
parent->nchildren++;
if(parent->type == Category && child->type == Category)
return;
if(parent->type == Work && child->type == Work)
return;
if(parent->type == Work && child->type == Track)
return;
if(parent->type == Track && child->type == File)
return;
if(child->parent == child)
return;
if(parent->type == Root)
return;
if(parent->parent->type == Root)
return;
// addcatparent(parent, child);
i = child->ncatparents;
if(0) fprint(2, "addcatparent %s parent %d type %d child %d type %d\n",where,
parent->tabno, parent->type, child->tabno, child->type);
child->catparents = realloc(child->catparents, (i+1)*sizeof parent);
child->catparents[i] = parent;
child->ncatparents++;
}
void
addcatparent(Object *parent, Object *child)
{
int i;
/* First check if child's already been added
* This saves checking elsewhere
*/
if(child->parent == child)
return;
// for(i = 0; i < child->ncatparents; i++)
// if(child->catparents[i] == parent) return;
i = child->ncatparents;
fprint(2, "addcatparent parent %d child %d\n", parent->tabno, child->tabno);
child->catparents = realloc(child->catparents, (i+1)*sizeof parent);
child->catparents[i] = parent;
child->ncatparents++;
}
void
sortprep(char *out, int n, Object *o)
{
char *p, *q;
if(*o->key)
q = o->key;
else if (o->value)
q = o->value;
else
q = "";
if(p = strchr(q, '~'))
p++;
else
p = q;
for(q = out; *p && q < out+n-1; q++)
*q = tolower(*p++);
*q = 0;
}
void
childsort(Object *o)
{
Object *oo;
int i, j, n;
char si[256], sj[256];
/* sort the kids by key or by value */
n = o->nchildren;
if(n > 1){
for(i = 0; i < n-1; i++){
sortprep(si, nelem(si), o->children[i]);
for(j = i+1; j < n; j++){
sortprep(sj, nelem(sj), o->children[j]);
if(strncmp(si, sj, sizeof(si)) > 0){
oo = o->children[i];
o->children[i] = o->children[j];
o->children[j] = oo;
strncpy(si, sj, sizeof(si));
}
}
}
}
}
void
childenum(Object *o){
Object *oo;
int i, n = 1;
for(i = 0; i < o->nchildren; i++){
oo = o->children[i];
if(tokenlist[oo->type].kind == Cat)
oo->num = n++;
else
switch(oo->type){
case Category:
case Part:
case Recording:
case Track:
case Work:
oo->num = n++;
default:
break;
}
}
}
Object *
newobject(Type t, Object *parent){
Object *o;
int tabno;
if(hotab){
for(tabno = 0; tabno < notab; tabno++)
if(otab[tabno] == nil)
break;
if(tabno == notab)
sysfatal("lost my hole");
hotab--;
}else{
if(sotab < notab+1){
sotab += 512;
otab = realloc(otab, sotab * sizeof o);
if(otab == nil)
sysfatal("realloc: %r");
}
tabno = notab++;
}
o = mallocz(sizeof(Object), 1);
o->tabno = tabno;
otab[tabno] = o;
o->type = t;
o->parent = parent;
if(parent && parent->path){
o->path = strdup(parent->path);
setmalloctag(o->path, 0x100008);
}
return o;
}
void
freeobject(Object *o, char*){
free(o->children);
if(o->orig == nil)
free(o->value);
free(o->path);
free(o->catparents);
catsetfree(&o->categories);
otab[o->tabno] = nil;
hotab++;
free(o);
}
void
freetree(Object *o)
{
int i;
for(i = 0; i < o->nchildren; i++)
if(o->children[i]->parent == o)
freetree(o->children[i]);
free(o->children);
if(o->orig == nil)
free(o->value);
free(o->path);
free(o->catparents);
catsetfree(&o->categories);
otab[o->tabno] = nil;
hotab++;
free(o);
}
|