Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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);
}