]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/music/jukefs/parse.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / games / music / jukefs / parse.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include <ctype.h>
6 #include "object.h"
7 #include "catset.h"
8 #include "parse.h"
9
10 #define MAXTOKEN 1024
11
12 Biobuf *f;
13 static int str;
14 char *file;
15
16 Token tokenlistinit[] = {
17         { "category",   Obj,    Category        , "music"       , {nil,0}},
18         { "cddata",     Obj,    Cddata          , nil           , {nil,0}},
19         { "command",    Obj,    Cmd             , nil           , {nil,0}},
20         { "file",       Obj,    File            , "file"        , {nil,0}},
21         { "include",    Obj,    Include         , nil           , {nil,0}},
22         { "key",        Obj,    Key             , nil           , {nil,0}},
23         { "lyrics",     Obj,    Lyrics          , "lyrics"      , {nil,0}},
24         { "part",       Obj,    Part            , "title"       , {nil,0}},
25         { "path",       Obj,    Path            , nil           , {nil,0}},
26         { "performance",Obj,    Performance     , "artist"      , {nil,0}},
27         { "recording",  Obj,    Recording       , "title"       , {nil,0}},
28         { "root",       Obj,    Root            , nil           , {nil,0}},
29         { "search",     Obj,    Search          , nil           , {nil,0}},
30         { "soloists",   Obj,    Soloists        , "artist"      , {nil,0}},
31         { "time",       Obj,    Time            , "time"        , {nil,0}},
32         { "track",      Obj,    Track           , "title"       , {nil,0}},
33         { "work",       Obj,    Work            , "title"       , {nil,0}},
34 };
35 Token *tokenlist;
36 int ntoken = nelem(tokenlistinit);
37 int catnr = 0;
38
39 Cmdlist cmdlist[] = {
40         {       Sort,   "sort"          },
41         {       Enum,   "number"        },
42         {       0x00,   0               },
43 };
44
45 static char *curtext;
46
47 void
48 inittokenlist(void)
49 {
50         int i;
51
52         ntoken = nelem(tokenlistinit);
53         tokenlist = malloc(sizeof(tokenlistinit));
54         memmove(tokenlist, tokenlistinit, sizeof(tokenlistinit));
55         for(i = 0; i< ntoken; i++){
56                 tokenlist[i].name = strdup(tokenlist[i].name);
57                 catsetinit(&tokenlist[i].categories, tokenlist[i].value);
58         }
59         curtext = smprint("{");
60 }
61
62 Type
63 gettoken(char *token)
64 {
65         char *p, *q;
66         int i, n;
67         Token *t;
68
69         for(;;){
70                 if(curtext){
71                         p = &curtext[strspn(curtext, " \t")];   
72                         if(*p && *p != '\n')
73                                 break;
74                 }
75                 do {
76                         str++;
77                         free(curtext);
78                         if((curtext = Brdstr(f, '\n', 0)) == nil)
79                                 return Eof;
80                 } while(curtext[0] == '#');
81         }
82         if(*p == '{'){
83                 *token++ = *p;
84                 *token = 0;
85                 *p = ' ';
86                 return BraceO;
87         }
88         if(*p == '}'){
89                 *token++ = *p;
90                 *token = 0;
91                 *p = ' ';
92                 return BraceC;
93         }
94         if(*p == '='){
95                 *token++ = *p;
96                 *token = 0;
97                 *p = ' ';
98                 return Equals;
99         }
100         t = nil;
101         n = 0;
102         for(i = 0; i < ntoken; i++){
103                 t = &tokenlist[i];
104                 if(strncmp(p, t->name, n=strlen(t->name)) == 0){
105                         q = &p[n];
106                                 if(isalnum(*q) || *q == '-') continue;
107                         q += strspn(q, " \t");
108                         if(t->kind == Obj && *q == '{')
109                                 break;
110                         if(t->kind == Cat && *q == '=')
111                                 break;
112                 }
113         }
114         if(i < ntoken){
115                 strcpy(token, t->name);
116                 memset(p, ' ', n);
117                 return i;
118         }
119         assert(strlen(token) < MAXTOKEN);
120         if(strchr(p, '{'))
121                 sysfatal("Illegal keyword or parse error: %s", p);
122         if((q = strchr(p, '='))){
123                 if(q == p) goto tx;
124                 *q = 0;
125                 strcpy(token, p);
126                 assert(strlen(token) < MAXTOKEN);
127                 memset(p, ' ', q-p);
128                 *q = '=';
129                 for(q = token; *q; q++)
130                         if(!isalnum(*q) && !isspace(*q)) break;
131                 if(*q) return Txt;
132                 while(isspace(*--q)) *q = 0;
133                 return Newcat;
134         }
135 tx:     if((q = strchr(p, '}'))){
136                 *q = 0;
137                 strcpy(token, p);
138                 assert(strlen(token) < MAXTOKEN);
139                 memset(p, ' ', q-p);
140                 *q = '}';
141                 return Txt;
142         }
143         strcpy(token, p);
144         assert(strlen(token) < MAXTOKEN);
145         free(curtext);
146         curtext = nil;
147         return Txt;
148 }
149
150 Object *
151 getobject(Type t, Object *parent)
152 {
153         char *token;
154         char *textbuf;
155         char *tp, *p, *q;
156         int i;
157         Object *o, *oo, *child;
158         Token *ot;
159         Type nt;
160
161         token = malloc(MAXTOKEN);
162         textbuf = malloc(8192);
163
164         tp = textbuf;
165         o = newobject(t, parent);
166         o->flags |= Hier;
167         if(parent == nil){
168                 root = o;
169                 o->path = strdup(startdir);
170                 setmalloctag(o->path, 0x100001);
171         }
172         if(gettoken(token) != BraceO)
173                 sysfatal("Parse error: no brace, str %d", str);
174         for(;;){
175                 t = gettoken(token);
176                 if(t >= 0)
177                         switch(tokenlist[t].kind){
178                         case Obj:
179                                 switch(t){
180                                 case Key:
181                                 case Cmd:
182                                 case Path:
183                                         if(getobject(t, o) != nil)
184                                                 sysfatal("Non-null child?");
185                                         break;
186                                 case Include:
187                                 case Category:
188                                         child = getobject(t, o);
189                                         if(child) addchild(o, child, "case Category");
190                                         break;
191                                 default:
192                                         /* subobject */
193                                         child = getobject(t, o);
194                                         if(child == nil)
195                                                 sysfatal("Null child?");
196                                         addchild(o, child, "default");
197                                         break;
198                                 }
199                                 break;
200                         case Cat:
201                         catcase:    nt = gettoken(token);
202                                 if(nt != Equals)
203                                         sysfatal("Expected Equals, not %s", token);
204                                 nt = gettoken(token);
205                                 if(nt != Txt)
206                                         sysfatal("Expected Text, not %s", token);
207                                 if((p = strchr(token, '\n'))) *p = 0;
208                                 p = token;
209                                 if(o->type == Category){
210                                         if(catsetisset(&o->categories)){
211                                                 fprint(2, "Category object must have one category\n");
212                                         }
213                                         catsetcopy(&o->categories, &tokenlist[t].categories);
214                                         strncpy(o->key, p, KEYLEN);
215                                         if(catobjects[t] == 0)
216                                                 sysfatal("Class %s not yet defined", tokenlist[t].name);
217                                         for(i = 0; i < catobjects[t]->nchildren; i++)
218                                                 if(strcmp(catobjects[t]->children[i]->key, p) == 0)
219                                                         break;
220                                         if(i == catobjects[t]->nchildren){
221                                                 /* It's a new key for the category */
222                                                 addchild(catobjects[t], o, "new key for cat");
223                                         }else{
224                                                 /* Key already existed */
225                                                 oo = catobjects[t]->children[i];
226                                                 if(oo->value)
227                                                         sysfatal("Duplicate category object for %s", oo->value);
228                                                 catobjects[t]->children[i] = o;
229                                                 if(oo->nchildren){
230                                                         for(i = 0; i < oo->nchildren; i++){
231                                                                 if(oo->children[i]->parent == oo)
232                                                                         oo->children[i]->parent = o;
233                                                                 addchild(o, oo->children[i], "key already existed");
234                                                         }
235                                                 }
236                                                 freeobject(oo, "a");
237                                         }
238                                         o->parent = catobjects[t];
239                                 }else{
240                                         catsetorset(&o->categories, &tokenlist[t].categories);
241                                         for(i = 0; i < catobjects[t]->nchildren; i++)
242                                                 if(strcmp(catobjects[t]->children[i]->key, p) == 0)
243                                                         break;
244                                         if(i == catobjects[t]->nchildren){
245                                                 oo = newobject(Category, catobjects[t]);
246 /*
247                                                 oo->value = strdup(token);
248 */
249                                                 strncpy(oo->key, p, KEYLEN);
250                                                 catsetcopy(&oo->categories, &tokenlist[t].categories);
251                                                 addchild(catobjects[t], oo, "catobjects[t],oo");
252                                         }
253                                         addchild(catobjects[t]->children[i], o, "children[i]");
254                                 }
255                                 break;
256                         }
257                 else
258                         switch(t){
259                         case Eof:
260                                 if(o->type == Root){
261                                         free(token);
262                                         free(textbuf);
263                                         return o;
264                                 }
265                                 sysfatal("Unexpected Eof in %s, file %s", tokenlist[o->type].name, file);
266                         case Newcat:
267                                 /* New category, make an entry in the tokenlist */
268                                 tokenlist = realloc(tokenlist, (ntoken+1)*sizeof(Token));
269                                 ot = &tokenlist[ntoken];
270                                 ot->name = strdup(token);
271                                 setmalloctag(ot->name, 0x100002);
272                                 ot->kind = Cat;
273                                 ot->value = -1;
274                                 memset(&ot->categories, 0, sizeof(Catset));
275                                 catsetinit(&ot->categories, catnr++);
276                                 /* And make an entry in the catobjects table */
277                                 if(ncat <= ntoken){
278                                         catobjects = realloc(catobjects, (ntoken+1)*sizeof(Object*));
279                                         while(ncat <= ntoken) catobjects[ncat++] = nil;
280                                 }
281                                 if(catobjects[ntoken] != nil)
282                                         sysfatal("Class %s already defined in %s:%d", token, file, str);
283                                 if(0) fprint(2, "newcat: token %s catnr %d ntoken %d ncat %d\n",
284                                         token, catnr, ntoken, ncat);
285                                 catobjects[ntoken] = newobject(Category, root);
286                                 if(o->type == Category)
287                                         catobjects[ntoken]->flags = o->flags&Hier;
288                                 catobjects[ntoken]->flags |= Sort;
289                                 strncpy(catobjects[ntoken]->key, token, KEYLEN);
290                                 catsetcopy(&catobjects[ntoken]->categories, &ot->categories);
291                                 addchild(root, catobjects[ntoken], "root");
292                                 t = ntoken;
293                                 ntoken++;
294                                 goto catcase;
295                         case Txt:
296                                 strcpy(tp, token);
297                                 tp += strlen(token);
298                                 break;
299                         case BraceC:
300                                 while(tp > textbuf && tp[-1] == '\n') *--tp = 0;
301                                 if((o->type == File || o->type == Include) && o->path){
302                                         o->value = smprint("%s/%s", o->path, textbuf);
303                                 }else if(tp > textbuf){
304                                         o->value = strdup(textbuf);
305                                         setmalloctag(o->value, 0x100003);
306                                 }
307                                 switch(o->type){
308                                 case Cmd:
309                                         q = strtok(o->value, " \t,;\n");
310                                         while(q){
311                                                 if(*q) for(i = 0; cmdlist[i].name; i++){
312                                                         if(strcmp(q, cmdlist[i].name) == 0){
313                                                                 o->parent->flags |= cmdlist[i].flag;
314                                                                 break;
315                                                         }
316                                                         if(cmdlist[i].name == 0)
317                                                                 fprint(2, "Unknown command: %s\n", q);
318                                                 }
319                                                 q = strtok(nil, " \t,;\n");
320                                         }
321                                         freeobject(o, "b");
322                                         free(token);
323                                         free(textbuf);
324                                         return nil;
325                                 case Path:
326                                         p = o->value;
327                                         free(o->parent->path);
328                                         if(p[0] == '/' || o->path == nil){
329                                                 o->parent->path = strdup(p);
330                                                 setmalloctag(o->parent->path, 0x100004);
331                                         }else{
332                                                 o->parent->path = smprint("%s/%s", o->path, p);
333                                                 setmalloctag(o->parent->path, 0x100005);
334                                         }
335                                         freeobject(o, "b");
336                                         free(token);
337                                         free(textbuf);
338                                         return nil;
339                                 case Include:
340                                         free(token);
341                                         free(textbuf);
342                                         return getinclude(o);
343                                 case Category:
344                                 /*
345                                         if(o->nchildren) break;
346                                  */
347                                         free(token);
348                                         free(textbuf);
349                                         return nil;
350                                 case Key:
351                                         strncpy(o->parent->key, o->value, KEYLEN);
352                                         freeobject(o, "d");
353                                         free(token);
354                                         free(textbuf);
355                                         return nil;
356                                 default:
357                                         break;
358                                 }
359                                 free(token);
360                                 free(textbuf);
361                                 return o;
362                         default:
363                                 fprint(2, "Unexpected token: %s\n", token);
364                                 free(token);
365                                 free(textbuf);
366                                 return nil;
367                         }
368         }
369 }
370
371 Object *
372 getinclude(Object *o)
373 {
374                 char *savetext;
375                 Biobuf *savef = f;
376                 char *savefile, fname[256];
377                 Object *oo;
378                 int savestr = str;
379                 char token[MAXTOKEN], *dirname, *filename;
380                 Type t;
381
382                 str = 0;
383                 if(curtext){
384                         savetext = strdup(curtext);
385                         setmalloctag(savetext, 0x100006);
386                 }else
387                         savetext = nil;
388                 if((f = Bopen(o->value, OREAD)) == nil)
389                         sysfatal("getinclude: %s: %r", o->value);
390                 savefile = file;
391                 file = strdup(o->value);
392                 strncpy(fname, o->value, 256);
393                 if((filename = strrchr(fname, '/'))){
394                         *filename = 0;
395                         dirname = fname;
396                         filename++;
397                 }else{
398                         dirname = "";
399                         filename = fname;
400                 }
401                 while((t = gettoken(token)) != Eof){
402                         if(t < 0){
403                                 if(*dirname)
404                                         sysfatal("Bad include file %s/%s, token %s, str %d",
405                                                 dirname, filename, token, str);
406                                 else
407                                         sysfatal("Bad include file %s, token %s, str %d",
408                                                 filename, token, str);
409                         }
410                         free(o->path);
411                         o->path = strdup(dirname);
412                         setmalloctag(o->path, 0x100007);
413                         oo = getobject(t, o->parent);
414                         if(oo) addchild(o->parent, oo, "o->parent, oo");
415                 }
416                 freeobject(o, "e");
417                 free(curtext);
418                 curtext = nil;
419                 if(savetext)
420                         curtext = savetext;
421                 free(file);
422                 file = savefile;
423                 str = savestr;
424                 Bterm(f);
425                 f = savef;
426                 return nil;
427 }
428
429 void
430 addchild(Object *parent, Object *child, char *where)
431 {
432                 int i;
433
434                 /* First check if child's already been added
435                  * This saves checking elsewhere
436                  */
437                 for(i = 0; i < parent->nchildren; i++)
438                                 if(parent->children[i] == child) return;
439                 parent->children = realloc(parent->children, (i+1)*4);
440                 parent->children[i] = child;
441                 parent->nchildren++;
442                 if(parent->type == Category && child->type == Category)
443                         return;
444                 if(parent->type == Work && child->type == Work)
445                         return;
446                 if(parent->type == Work && child->type == Track)
447                         return;
448                 if(parent->type == Track && child->type == File)
449                         return;
450                 if(child->parent == child)
451                         return;
452                 if(parent->type == Root)
453                         return;
454                 if(parent->parent->type == Root)
455                         return;
456 //              addcatparent(parent, child);
457                 i = child->ncatparents;
458                 if(0) fprint(2, "addcatparent %s parent %d type %d child %d type %d\n",where,
459                         parent->tabno, parent->type, child->tabno, child->type);
460                 child->catparents = realloc(child->catparents, (i+1)*4);
461                 child->catparents[i] = parent;
462                 child->ncatparents++;
463 }
464
465 void
466 addcatparent(Object *parent, Object *child)
467 {
468                 int i;
469
470                 /* First check if child's already been added
471                  * This saves checking elsewhere
472                  */
473                 if(child->parent == child)
474                         return;
475 //              for(i = 0; i < child->ncatparents; i++)
476 //                              if(child->catparents[i] == parent) return;
477                 i = child->ncatparents;
478                 fprint(2, "addcatparent parent %d child %d\n", parent->tabno, child->tabno);
479                 child->catparents = realloc(child->catparents, (i+1)*4);
480                 child->catparents[i] = parent;
481                 child->ncatparents++;
482 }
483
484 void
485 sortprep(char *out, int n, Object *o)
486 {
487         char *p, *q;
488
489         if(*o->key)
490                 q = o->key;
491         else if (o->value)
492                 q = o->value;
493         else
494                 q = "";
495         if(p = strchr(q, '~'))
496                 p++;
497         else
498                 p = q;
499         for(q = out; *p && q < out+n-1; q++)
500                 *q = tolower(*p++);
501         *q = 0;
502 }
503
504 void
505 childsort(Object *o)
506 {
507                 Object *oo;
508                 int i, j, n;
509                 char si[256], sj[256];
510                 /* sort the kids by key or by value */
511
512                 n = o->nchildren;
513                 if(n > 1){
514                         for(i = 0; i < n-1; i++){
515                                 sortprep(si, nelem(si), o->children[i]);
516                                 for(j = i+1; j < n; j++){
517                                         sortprep(sj, nelem(sj), o->children[j]);
518                                         if(strncmp(si, sj, sizeof(si)) > 0){
519                                                 oo = o->children[i];
520                                                 o->children[i] = o->children[j];
521                                                 o->children[j] = oo;
522                                                 strncpy(si, sj, sizeof(si));
523                                         }
524                                 }
525                         }
526                 }
527 }
528
529 void
530 childenum(Object *o){
531                 Object *oo;
532                 int i, n = 1;
533
534                 for(i = 0; i < o->nchildren; i++){
535                         oo = o->children[i];
536                         if(tokenlist[oo->type].kind == Cat)
537                                 oo->num = n++;
538                         else
539                                 switch(oo->type){
540                                 case Category:
541                                 case Part:
542                                 case Recording:
543                                 case Track:
544                                 case Work:
545                                         oo->num = n++;
546                                 default:
547                                         break;
548                                 }
549                 }
550 }
551
552 Object *
553 newobject(Type t, Object *parent){
554         Object *o;
555         int tabno;
556
557         if(hotab){
558                 for(tabno = 0; tabno < notab; tabno++)
559                         if(otab[tabno] == nil)
560                                 break;
561                 if(tabno == notab)
562                         sysfatal("lost my hole");
563                 hotab--;
564         }else{
565                 if(sotab < notab+1){
566                         sotab += 512;
567                         otab = realloc(otab, sizeof(Object*)*sotab);
568                         if(otab == nil)
569                                 sysfatal("realloc: %r");
570                 }
571                 tabno = notab++;
572         }
573         o = mallocz(sizeof(Object), 1);
574         o->tabno = tabno;
575         otab[tabno] = o;
576         o->type = t;
577         o->parent = parent;
578         if(parent && parent->path){
579                 o->path = strdup(parent->path);
580                 setmalloctag(o->path, 0x100008);
581         }
582         return o;
583 }
584
585 void
586 freeobject(Object *o, char*){
587
588         free(o->children);
589         if(o->orig == nil)
590                 free(o->value);
591         free(o->path);
592         free(o->catparents);
593         catsetfree(&o->categories);
594         otab[o->tabno] = nil;
595         hotab++;
596         free(o);
597 }
598
599 void
600 freetree(Object *o)
601 {
602         int i;
603
604         for(i = 0; i < o->nchildren; i++)
605                 if(o->children[i]->parent == o)
606                         freetree(o->children[i]);
607         free(o->children);
608         if(o->orig == nil)
609                 free(o->value);
610         free(o->path);
611         free(o->catparents);
612         catsetfree(&o->categories);
613         otab[o->tabno] = nil;
614         hotab++;
615         free(o);
616 }