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
inittokenlist(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
gettoken(char * token)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 *
getobject(Type t,Object * parent)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 *
getinclude(Object * o)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
addchild(Object * parent,Object * child,char * where)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)*sizeof child);
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)*sizeof parent);
461 child->catparents[i] = parent;
462 child->ncatparents++;
463 }
464
465 void
addcatparent(Object * parent,Object * child)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)*sizeof parent);
480 child->catparents[i] = parent;
481 child->ncatparents++;
482 }
483
484 void
sortprep(char * out,int n,Object * o)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
childsort(Object * o)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
childenum(Object * o)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 *
newobject(Type t,Object * parent)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, sotab * sizeof o);
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
freeobject(Object * o,char *)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
freetree(Object * o)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 }
617