xref: /plan9-contrib/sys/src/games/music/jukefs/parse.c (revision 6ca6a3e703ee2ec4aed99c2177f71d7f127da6d9)
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