xref: /inferno-os/libtk/parse.c (revision 54d06533f3d8d73d9369206f7f5be71351cfbcf3)
1 #include "lib9.h"
2 #include "kernel.h"
3 #include "draw.h"
4 #include "tk.h"
5 
6 #define	O(t, e)		((long)(&((t*)0)->e))
7 
8 static char* pdist(TkTop*, TkOption*, void*, char**, char*, char*);
9 static char* pstab(TkTop*, TkOption*, void*, char**, char*, char*);
10 static char* ptext(TkTop*, TkOption*, void*, char**, char*, char*);
11 static char* pwinp(TkTop*, TkOption*, void*, char**, char*, char*);
12 static char* pbmap(TkTop*, TkOption*, void*, char**, char*, char*);
13 static char* pbool(TkTop*, TkOption*, void*, char**, char*, char*);
14 static char* pfont(TkTop*, TkOption*, void*, char**, char*, char*);
15 static char* pfrac(TkTop*, TkOption*, void*, char**, char*, char*);
16 static char* pnnfrac(TkTop*, TkOption*, void*, char**, char*, char*);
17 static char* pctag(TkTop*, TkOption*, void*, char**, char*, char*);
18 static char* ptabs(TkTop*, TkOption*, void*, char**, char*, char*);
19 static char* pcolr(TkTop*, TkOption*, void*, char**, char*, char*);
20 static char* pimag(TkTop*, TkOption*, void*, char**, char*, char*);
21 static char* psize(TkTop*, TkOption*, void*, char**, char*, char*);
22 static char* pnndist(TkTop*, TkOption*, void*, char**, char*, char*);
23 static char* pact(TkTop*, TkOption*, void*, char**, char*, char*);
24 static char* pignore(TkTop*, TkOption*, void*, char**, char*, char*);
25 static char* psticky(TkTop*, TkOption*, void*, char**, char*, char*);
26 static char* plist(TkTop*, TkOption*, void*, char**, char*, char*);
27 
28 static char* (*oparse[])(TkTop*, TkOption*, void*, char**, char*, char*) =
29 {
30 	/* OPTdist */	pdist,
31 	/* OPTstab */	pstab,
32 	/* OPTtext */	ptext,
33 	/* OPTwinp */	pwinp,
34 	/* OPTflag */	pstab,
35 	/* OPTbmap */	pbmap,
36 	/* OPTbool */	pbool,
37 	/* OPTfont */	pfont,
38 	/* OPTfrac */	pfrac,
39 	/* OPTnnfrac */	pnnfrac,
40 	/* OPTctag */	pctag,
41 	/* OPTtabs */	ptabs,
42 	/* OPTcolr */	pcolr,
43 	/* OPTimag */	pimag,
44 	/* OPTsize */	psize,
45 	/* OPTnndist */	pnndist,
46 	/* OPTact */	pact,
47 	/* OPTignore */	pignore,
48 	/* OPTsticky */	psticky,
49 	/* OPTlist */ plist,
50 	/* OPTflags */	pstab,
51 };
52 
53 char*
54 tkskip(char *s, char *bl)
55 {
56 	char *p;
57 
58 	while(*s) {
59 		for(p = bl; *p; p++)
60 			if(*p == *s)
61 				break;
62 		if(*p == '\0')
63 			return s;
64 		s++;
65 	}
66 	return s;
67 }
68 
69 /* XXX - Tad: error propagation? */
70 char*
71 tkword(TkTop *t, char *str, char *buf, char *ebuf, int *gotarg)
72 {
73 	int c, lev, tmp;
74 	char *val, *e, *p, *cmd;
75 	if (gotarg == nil)
76 		gotarg = &tmp;
77 
78 	/*
79 	 * ebuf is one beyond last byte in buf; leave room for nul byte in
80 	 * all cases.
81 	 */
82 	--ebuf;
83 
84 	str = tkskip(str, " \t");
85 	*gotarg = 1;
86 	lev = 1;
87 	switch(*str) {
88 	case '{':
89 		/* XXX - DBK: According to Ousterhout (p.37), while back=
90 		 * slashed braces don't count toward finding the matching
91 		 * closing braces, the backslashes should not be removed.
92 		 * Presumably this also applies to other backslashed
93 		 * characters: the backslash should not be removed.
94 		 */
95 		str++;
96 		while(*str && buf < ebuf) {
97 			c = *str++;
98 			if(c == '\\') {
99 				if(*str == '}' || *str == '{' || *str == '\\')
100 					c = *str++;
101 			} else if(c == '}') {
102 				lev--;
103 				if(lev == 0)
104 					break;
105 			} else if(c == '{')
106 				lev++;
107 			*buf++ = c;
108 		}
109 		break;
110 	case '[':
111 		/* XXX - DBK: According to Ousterhout (p. 33) command
112 		 * substitution may occur anywhere within a word, not
113 		 * only (as here) at the beginning.
114 		 */
115 		cmd = malloc(strlen(str));	/* not strlen+1 because the first character is skipped */
116 		if ( cmd == nil ) {
117 			buf[0] = '\0';	/* DBK - Why not an error message? */
118 			return str;
119 		}
120 		p = cmd;
121 		str++;
122 		while(*str) {
123 			c = *str++;
124 			if(c == '\\') {
125 				if(*str == ']' || *str == '[' || *str == '\\')
126 					c = *str++;
127 			} else if(c == ']') {
128 				lev--;
129 				if(lev == 0)
130 					break;
131 			} else if(c == '[')
132 				lev++;
133 			*p++ = c;
134 		}
135 		*p = '\0';
136 		val = nil;
137 		e = tkexec(t, cmd, &val);
138 		free(cmd);
139 		 /* XXX - Tad: is this appropriate behavior?
140 		  *	      Am I sure that the error doesn't need to be
141 		  *	      propagated back to the caller?
142 		  */
143 		if(e == nil && val != nil) {
144 			strncpy(buf, val, ebuf-buf);
145 			buf = ebuf;
146 			free(val);
147 		}
148 		break;
149 	case '\'':
150 		str++;
151 		while(*str && buf < ebuf)
152 			*buf++ = *str++;
153 		break;
154 	case '\0':
155 		*gotarg = 0;
156 		break;
157 	default:
158 		/* XXX - DBK: See comment above about command substitution.
159 		 * Also, any backslashed character should be replaced by
160 		 * itself (e.g. to put a space, tab, or [ into a word.
161 		 * We assume that the C compiler has already done the
162 		 * standard ANSI C substitutions.  (But should we?)
163 		 */
164 		while(*str && *str != ' ' && *str != '\t' && buf < ebuf)
165 			*buf++ = *str++;
166 	}
167 	*buf = '\0';
168 	return str;
169 }
170 
171 static TkOption*
172 Getopt(TkOption *o, char *buf)
173 {
174 	while(o->o != nil) {
175 		if(strcmp(buf, o->o) == 0)
176 			return o;
177 		o++;
178 	}
179 	return nil;
180 }
181 
182 TkName*
183 tkmkname(char *name)
184 {
185 	TkName *n;
186 
187 	n = malloc(sizeof(struct TkName)+strlen(name));
188 	if(n == nil)
189 		return nil;
190 	strcpy(n->name, name);
191 	n->link = nil;
192 	n->obj = nil;
193 	return n;
194 }
195 
196 char*
197 tkparse(TkTop *t, char *str, TkOptab *ot, TkName **nl)
198 {
199 	int l;
200 	TkOptab *ft;
201 	TkOption *o;
202 	TkName *f, *n;
203 	char *e, *buf, *ebuf;
204 
205 	l = strlen(str);
206 	if (l < Tkmaxitem)
207 		l = Tkmaxitem;
208 	buf = malloc(l + 1);
209 	if(buf == 0)
210 		return TkNomem;
211 	ebuf = buf + l + 1;
212 
213 	e = nil;
214 	while(e == nil) {
215 		str = tkword(t, str, buf, ebuf, nil);
216 		switch(*buf) {
217 		case '\0':
218 			goto done;
219 		case '-':
220 			if (buf[1] != '\0') {
221 				for(ft = ot; ft->ptr; ft++) {
222 					o = Getopt(ft->optab, buf+1);
223 					if(o != nil) {
224 						e = oparse[o->type](t, o, ft->ptr, &str, buf, ebuf);
225 						break;
226 					}
227 				}
228 				if(ft->ptr == nil){
229 					e = TkBadop;
230 					tkerr(t, buf);
231 				}
232 				break;
233 			}
234 			/* fall through if we've got a singleton '-' */
235 		default:
236 			if(nl == nil) {
237 				e = TkBadop;
238 				tkerr(t, buf);
239 				break;
240 			}
241 			n = tkmkname(buf);
242 			if(n == nil) {
243 				e = TkNomem;
244 				break;
245 			}
246 			if(*nl == nil)
247 				*nl = n;
248 			else {
249 				for(f = *nl; f->link; f = f->link)
250 					;
251 				f->link = n;
252 			}
253 		}
254 	}
255 
256 	if(e != nil && nl != nil)
257 		tkfreename(*nl);
258 done:
259 	free(buf);
260 	return e;
261 }
262 
263 char*
264 tkconflist(TkOptab *ot, char **val)
265 {
266 	TkOption *o;
267 	char *f, *e;
268 
269 	f = "-%s";
270 	while(ot->ptr != nil) {
271 		o = ot->optab;
272 		while(o->o != nil) {
273 			e = tkvalue(val, f, o->o);
274 			if(e != nil)
275 				return e;
276 			f = " -%s";
277 			o++;
278 		}
279 		ot++;
280 	}
281 	return nil;
282 }
283 
284 char*
285 tkgencget(TkOptab *ft, char *arg, char **val, TkTop *t)
286 {
287 	Tk *w;
288 	char *c;
289 	Point g;
290 	TkEnv *e;
291 	TkStab *s;
292 	TkOption *o;
293 	int wh, con, i, n, flag, *v;
294 	char *r, *buf, *fmt, *out;
295 
296 	buf = mallocz(Tkmaxitem, 0);
297 	if(buf == nil)
298 		return TkNomem;
299 
300 	tkitem(buf, arg);
301 	r = buf;
302 	if(*r == '-')
303 		r++;
304 	o = nil;
305 	while(ft->ptr) {
306 		o = Getopt(ft->optab, r);
307 		if(o != nil)
308 			break;
309 		ft++;
310 	}
311 	if(o == nil) {
312 		tkerr(t, r);
313 		free(buf);
314 		return TkBadop;
315 	}
316 
317 	switch(o->type) {
318 	default:
319 		tkerr(t, r);
320 		free(buf);
321 		return TkBadop;
322 	case OPTignore:
323 		return nil;
324 	case OPTact:
325 		w = ft->ptr;
326 		g = tkposn(w);
327 		n = g.y;
328 		if(o->aux == 0)
329 			n = g.x;
330 		free(buf);
331 		return tkvalue(val, "%d", n);
332 	case OPTdist:
333 	case OPTnndist:
334 		free(buf);
335 		return tkvalue(val, "%d", OPTION(ft->ptr, int, o->offset));
336 	case OPTsize:
337 		w = ft->ptr;
338 		if(strcmp(r, "width") == 0)
339 			wh = w->req.width;
340 		else
341 			wh = w->req.height;
342 		free(buf);
343 		return tkvalue(val, "%d", wh);
344 	case OPTtext:
345 		c = OPTION(ft->ptr, char*, o->offset);
346 		if(c == nil)
347 			c = "";
348 		free(buf);
349 		return tkvalue(val, "%s", c);
350 	case OPTwinp:
351 		w = OPTION(ft->ptr, Tk*, o->offset);
352 		if(w == nil || w->name == nil)
353 			c = "";
354 		else
355 			c = w->name->name;
356 		free(buf);
357 		return tkvalue(val, "%s", c);
358 	case OPTstab:
359 		s = o->aux;
360 		c = "";
361 		con = OPTION(ft->ptr, int, o->offset);
362 		while(s->val) {
363 			if(con == s->con) {
364 				c = s->val;
365 				break;
366 			}
367 			s++;
368 		}
369 		free(buf);
370 		return tkvalue(val, "%s", c);
371 	case OPTflag:
372 		con = OPTION(ft->ptr, int, o->offset);
373 		flag = 0;
374 		for (s = o->aux; s->val != nil; s++)
375 			flag |= s->con;
376 		c = "";
377 		for (s = o->aux; s->val != nil; s++) {
378 			if ((con & flag) == s->con) {
379 				c = s->val;
380 				break;
381 			}
382 		}
383 		free(buf);
384 		return tkvalue(val, "%s", c);
385 	case OPTflags:
386 		con = OPTION(ft->ptr, int, o->offset);
387 		out = mallocz(Tkmaxitem, 0);
388 		if(out == nil) {
389 			free(buf);
390 			return TkNomem;
391 		}
392 		c = out;
393 		for (s = o->aux; s->val != nil; s++) {
394 			if (s->con == (s->con&-s->con) && (con & s->con) != 0)
395 				c = seprint(c, out+Tkmaxitem, " %s", s->val);	/* should this be quoted? */
396 		}
397 		free(buf);
398 		*c = 0;
399 		r = tkvalue(val, "%s", out);
400 		free(out);
401 		return r;
402 	case OPTfont:
403 		e = OPTION(ft->ptr, TkEnv*, o->offset);
404 		free(buf);
405 		if (e->font != nil)
406 			return tkvalue(val, "%s", e->font->name);
407 		return nil;
408 	case OPTcolr:
409 		e = OPTION(ft->ptr, TkEnv*, o->offset);
410 		i = AUXI(o->aux);
411 		free(buf);
412 		return tkvalue(val, "#%.8lux", e->colors[i]);
413 	case OPTfrac:
414 	case OPTnnfrac:
415 		v = &OPTION(ft->ptr, int, o->offset);
416 		n = (int)o->aux;
417 		if(n == 0)
418 			n = 1;
419 		fmt = "%s";
420 		for(i = 0; i < n; i++) {
421 			tkfprint(buf, *v++);
422 			r = tkvalue(val, fmt, buf);
423 			if(r != nil) {
424 				free(buf);
425 				return r;
426 			}
427 			fmt = " %s";
428 		}
429 		free(buf);
430 		return nil;
431 	case OPTbmap:
432 		//free(buf);
433 		return tkvalue(val, "%d", OPTION(ft->ptr, Image*, o->offset) != nil);
434 	case OPTimag:
435 		//free(buf);
436 		return tkvalue(val, "%d", OPTION(ft->ptr, TkImg*, o->offset) != nil);
437 	}
438 }
439 
440 static char*
441 pact(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
442 {
443 	USED(buf);
444 	USED(ebuf);
445 	USED(str);
446 	USED(place);
447 	tkerr(t, o->o);
448 	return TkBadop;
449 }
450 
451 static char*
452 pignore(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
453 {
454 	char *p;
455 	USED(t);
456 	USED(o);
457 	USED(place);
458 
459 	p = tkword(t, *str, buf, ebuf, nil);
460 	if(*buf == '\0')
461 		return TkOparg;
462 	*str = p;
463 	return nil;
464 }
465 
466 static char*
467 pdist(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
468 {
469 	int d;
470 	char *e;
471 	TkEnv *env;
472 
473 	USED(buf);
474 	USED(ebuf);
475 
476 	/*
477 	 * this is a bit of a hack, as 0 is a valid option offset,
478 	 * but a nil aux is commonly used when 'w' and 'h' suffixes
479 	 * aren't appropriate.
480 	 * just make sure that no structure placed in TkOptab->ptr
481 	 * with an OPTdist element has a TkEnv as its first member.
482 	 */
483 
484 	if (o->aux == nil)
485 		env = nil;
486 	else
487 		env = OPTION(place, TkEnv*, AUXI(o->aux));
488 	e = tkfracword(t, str, &d, env);
489 	if(e != nil)
490 		return e;
491 	OPTION(place, int, o->offset) = TKF2I(d);
492 	return nil;
493 }
494 
495 static char*
496 pnndist(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
497 {
498 	char* e;
499 	int oldv;
500 
501 	oldv = OPTION(place, int, o->offset);
502 	e = pdist(t, o, place, str, buf, ebuf);
503 	if(e == nil && OPTION(place, int, o->offset) < 0) {
504 		OPTION(place, int, o->offset) = oldv;
505 		return TkBadvl;
506 	}
507 	return e;
508 }
509 
510 static char*
511 psize(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
512 {
513 	Tk *tk;
514 	char *e;
515 	int d, off;
516 
517 	USED(ebuf);
518 	e = tkfracword(t, str, &d, OPTION(place, TkEnv*, AUXI(o->aux)));
519 	if (e != nil)
520 		return e;
521 	if(d < 0)
522 		return TkBadvl;
523 
524 	tk = place;
525 	/*
526 	 * XXX there's no way of resetting Tksetwidth or Tksetheight.
527 	 * could perhaps allow it by setting width/height to {}
528 	 */
529 	if(strcmp(buf+1, "width") == 0) {
530 		tk->flag |= Tksetwidth;
531 		off = O(Tk, req.width);
532 	}
533 	else {
534 		tk->flag |= Tksetheight;
535 		off = O(Tk, req.height);
536 	}
537 	OPTION(place, int, off) = TKF2I(d);
538 	return nil;
539 }
540 
541 static TkStab*
542 lookstab(TkStab *s, char *word)
543 {
544 	for(; s->val != nil; s++)
545 		if(strcmp(s->val, word) == 0)
546 			return s;
547 	return nil;
548 }
549 
550 static char*
551 pstab(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
552 {
553 	char *p, *fields[8];
554 	int mask, val, nf;
555 	TkStab *s, *c;
556 
557 	p = tkword(t, *str, buf, ebuf, nil);
558 	if(*buf == '\0')
559 		return TkOparg;
560 
561 	if(o->type == OPTstab) {
562 		s = lookstab(o->aux, buf);
563 		if(s == nil)
564 			return TkBadvl;
565 		*str = p;
566 		OPTION(place, int, o->offset) = s->con;
567 		return nil;
568 	}
569 
570 	nf = getfields(buf, fields, nelem(fields), 1, " \t,");
571 	if(nf < 1 || nf > 1 && o->type != OPTflags)
572 		return TkBadvl;
573 
574 	mask = 0;
575 	for(c = o->aux; c->val; c++)
576 		mask |= c->con;
577 
578 	val = 0;
579 	while(--nf >= 0) {
580 		s = lookstab(o->aux, fields[nf]);
581 		if(s == nil)
582 			return TkBadvl;
583 		val |= s->con;
584 	}
585 	*str = p;
586 
587 	OPTION(place, int, o->offset) &= ~mask;
588 	OPTION(place, int, o->offset) |= val;
589 
590 	/*
591 	 * a hack, but otherwise we have to dirty the focus order
592 	 * every time any command is executed on a widget
593 	 */
594 	if(strcmp(o->o, "takefocus") == 0)
595 		tkdirtyfocusorder(t);
596 	return nil;
597 }
598 
599 enum {
600 	Stickyn = (1<<0),
601 	Stickye = (1<<1),
602 	Stickys = (1<<2),
603 	Stickyw = (1<<3)
604 };
605 
606 static int stickymap[16] =
607 {
608 	0,
609 	Tknorth,
610 	Tkeast,
611 	Tknorth|Tkeast,
612 	Tksouth,
613 	Tkfilly,
614 	Tksouth|Tkeast,
615 	Tkeast|Tkfilly,
616 	Tkwest,
617 	Tknorth|Tkwest,
618 	Tkfillx,
619 	Tknorth|Tkfillx,
620 	Tksouth|Tkwest,
621 	Tkwest|Tkfilly,
622 	Tksouth|Tkfillx,
623 	Tkfillx|Tkfilly,
624 };
625 
626 static char*
627 psticky(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
628 {
629 	char *p, *s;
630 	int flag, sflag;
631 
632 	p = tkword(t, *str, buf, ebuf, nil);
633 	*str = p;
634 
635 	flag = 0;
636 	for (s = buf; *s; s++) {
637 		switch (*s) {
638 		case 'n':
639 			flag |= Stickyn;
640 			break;
641 		case 's':
642 			flag |= Stickys;
643 			break;
644 		case 'e':
645 			flag |= Stickye;
646 			break;
647 		case 'w':
648 			flag |= Stickyw;
649 			break;
650 		case ' ':
651 		case ',':
652 			break;
653 		default:
654 			return TkBadvl;
655 		}
656 	}
657 	sflag =  OPTION(place, int, o->offset) & ~(Tkanchor|Tkfill);
658 	OPTION(place, int, o->offset) = sflag | stickymap[flag];
659 	return nil;
660 }
661 
662 static char*
663 ptext(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
664 {
665 	char **p;
666 
667 	*str = tkword(t, *str, buf, ebuf, nil);
668 
669 	p = &OPTION(place, char*, o->offset);
670 	if(*p != nil)
671 		free(*p);
672 	if(buf[0] == '\0')
673 		*p = nil;
674 	else {
675 		*p = strdup(buf);
676 		if(*p == nil)
677 			return TkNomem;
678 	}
679 	return nil;
680 }
681 
682 static char*
683 pimag(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
684 {
685 	int locked;
686 	Display *d;
687 	TkImg **p, *i;
688 
689 	i = nil;
690 	p = &OPTION(place, TkImg*, o->offset);
691 	*str = tkword(t, *str, buf, ebuf, nil);
692 	if(*buf != '\0') {
693 		i = tkname2img(t, buf);
694 		if(i == nil)
695 			return TkBadvl;
696 		i->ref++;
697 	}
698 
699 	if(*p != nil) {
700 		d = t->display;
701 		locked = lockdisplay(d);
702 		tkimgput(*p);
703 		if(locked)
704 			unlockdisplay(d);
705 	}
706 	*p = i;
707 	return nil;
708 }
709 
710 static char*
711 pbmap(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
712 {
713 	Display *d;
714 	Image *i, **p;
715 	int locked, fd;
716 	char *c;
717 
718 	p = &OPTION(place, Image*, o->offset);
719 
720 	d = t->display;
721 	*str = tkword(t, *str, buf, ebuf, nil);
722 	if(*buf == '\0' || *buf == '-') {
723 		if(*p != nil) {
724 			locked = lockdisplay(d);
725 			freeimage(*p);
726 			if(locked)
727 				unlockdisplay(d);
728 			*p = nil;
729 		}
730 		return nil;
731 	}
732 
733 	if(buf[0] == '@')
734 		i = display_open(d, buf+1);
735  else if(buf[0] == '<') {
736 		buf++;
737 		fd = strtoul(buf, &c, 0);
738 		if(c == buf) {
739 			return TkBadvl;
740 		}
741 		i = readimage(d, fd, 1);
742 	}
743 	else {
744 		char *file;
745 
746 		file = mallocz(Tkmaxitem, 0);
747 		if(file == nil)
748 			return TkNomem;
749 
750 		snprint(file, Tkmaxitem, "/icons/tk/%s", buf);
751 		i = display_open(d, file);
752 		free(file);
753 	}
754 	if(i == nil)
755 		return TkBadbm;
756 
757 	if(*p != nil) {
758 		locked = lockdisplay(d);
759 		freeimage(*p);
760 		if(locked)
761 			unlockdisplay(d);
762 	}
763 	*p = i;
764 	return nil;
765 }
766 
767 static char*
768 pfont(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
769 {
770 	TkEnv *e;
771 	Display *d;
772 	int locked;
773 	Font *font;
774 
775 	*str = tkword(t, *str, buf, ebuf, nil);
776 	if(*buf == '\0')
777 		return TkOparg;
778 
779 	d = t->display;
780 	font = font_open(d, buf);
781 	if(font == nil)
782 		return TkBadft;
783 
784 	e = tkdupenv(&OPTION(place, TkEnv*, o->offset));
785 	if(e == nil) {
786 		freefont(font);		/* XXX lockdisplay around this? */
787 		return TkNomem;
788 	}
789 	if(e->font)
790 		font_close(e->font);
791 	e->font = font;
792 
793 	locked = lockdisplay(d);
794 	e->wzero = stringwidth(font, "0");
795 	if ( e->wzero <= 0 )
796 		e->wzero = e->font->height / 2;
797 	if(locked)
798 		unlockdisplay(d);
799 
800 	return nil;
801 }
802 
803 static int
804 hex(int c)
805 {
806 	if(c >= 'a')
807 		c -= 'a'-'A';
808 	if(c >= 'A')
809 		c = 10 + (c - 'A');
810 	else
811 		c -= '0';
812 	return c;
813 }
814 
815 static ulong
816 changecol(TkEnv *e, int setcol, int col, ulong rgba)
817 {
818 	if (setcol) {
819 		e->set |= (1<<col);
820 	} else {
821 		rgba = 0;
822 		e->set &= ~(1<<col);
823 	}
824 	e->colors[col] = rgba;
825 	return rgba;
826 }
827 
828 char*
829 tkparsecolor(char *buf, ulong *rgba)
830 {
831 	char *p, *q, *e;
832 	int R, G, B, A;
833 	int i, alpha, len, alen;
834 	/*
835 	 * look for alpha modifier in *#AA or *0.5 format
836 	 */
837 	len = strlen(buf);
838 	p = strchr(buf, '*');
839 	if(p != nil) {
840 		alen = len - (p - buf);
841 		if(p[1] == '#') {
842 			if(alen != 4)
843 				return TkBadvl;
844 			alpha = (hex(p[2])<<4) | (hex(p[3]));
845 		} else {
846 			q = p+1;
847 			e = tkfrac(&q, &alpha, nil);
848 			if (e != nil)
849 				return e;
850 			alpha = TKF2I(alpha * 0xff);
851 		}
852 		*p = '\0';
853 		len -= alen;
854 	} else
855 		alpha = 0xff;
856 
857 	if (*buf == '#') {
858 		switch(len) {
859 		case 4:			/* #RGB */
860 			R = hex(buf[1]);
861 			G = hex(buf[2]);
862 			B = hex(buf[3]);
863 			*rgba = (R<<28) | (G<<20) | (B<<12) | 0xff;
864 			break;
865 		case 7:			/* #RRGGBB */
866 			R = (hex(buf[1])<<4)|(hex(buf[2]));
867 			G = (hex(buf[3])<<4)|(hex(buf[4]));
868 			B = (hex(buf[5])<<4)|(hex(buf[6]));
869 			*rgba = (R<<24) | (G<<16) | (B<<8) | 0xff;
870 			break;
871 		case 9:			/* #RRGGBBAA */
872 			R = (hex(buf[1])<<4)|(hex(buf[2]));
873 			G = (hex(buf[3])<<4)|(hex(buf[4]));
874 			B = (hex(buf[5])<<4)|(hex(buf[6]));
875 			A = (hex(buf[7])<<4)|(hex(buf[8]));
876 			*rgba = (R<<24) | (G<<16) | (B<<8) | A;
877 			break;
878 		default:
879 			return TkBadvl;
880 		}
881 	} else {
882 		for(i = 0; tkcolortab[i].val != nil; i++)
883 			if (!strcmp(tkcolortab[i].val, buf))
884 				break;
885 		if (tkcolortab[i].val == nil)
886 			return TkBadvl;
887 		*rgba = tkcolortab[i].con;
888 	}
889 	if (alpha != 0xff) {
890 		tkrgbavals(*rgba, &R, &G, &B, &A);
891 		A = (A * alpha) / 255;
892 		*rgba = tkrgba(R, G, B, A);
893 	}
894 	return nil;
895 }
896 
897 static char*
898 pcolr(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
899 {
900 	TkEnv *env;
901 	char *e;
902 	ulong rgba, dark, light;
903 	int color, setcol;
904 
905 	*str = tkword(t, *str, buf, ebuf, nil);
906 	rgba = 0;
907 	if(*buf == '\0') {
908 		setcol = 0;
909 	} else {
910 		setcol = 1;
911 		e = tkparsecolor(buf, &rgba);
912 		if(e != nil)
913 			return e;
914 	}
915 
916 	env = tkdupenv(&OPTION(place, TkEnv*, o->offset));
917 	if(env == nil)
918 		return TkNomem;
919 
920 	color = AUXI(o->aux);
921 	rgba = changecol(env, setcol, color, rgba);
922 	if(color == TkCbackgnd || color == TkCselectbgnd || color == TkCactivebgnd) {
923 		if (setcol) {
924 			light = tkrgbashade(rgba, TkLightshade);
925 			dark = tkrgbashade(rgba, TkDarkshade);
926 		} else
927 			light = dark = 0;
928 		changecol(env, setcol, color+1, light);
929 		changecol(env, setcol, color+2, dark);
930 	}
931 	return nil;
932 }
933 
934 static char*
935 pbool(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
936 {
937 	USED(buf);
938 	USED(ebuf);
939 	USED(str);
940 	USED(t);
941 	OPTION(place, int, o->offset) = 1;
942 	return nil;
943 }
944 
945 static char*
946 pwinp(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
947 {
948 	Tk *f;
949 	char *p;
950 
951 	p = tkword(t, *str, buf, ebuf, nil);
952 	if(*buf == '\0')
953 		return TkOparg;
954 	*str = p;
955 
956 	f = tklook(t, buf, 0);
957 	if(f == nil){
958 		tkerr(t, buf);
959 		return TkBadwp;
960 	}
961 
962 	OPTION(place, Tk*, o->offset) = f;
963 	return nil;
964 }
965 
966 static char*
967 pctag(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
968 {
969 	char *p;
970 	TkName *n, *l;
971 
972 	*str = tkword(t, *str, buf, ebuf, nil);
973 
974 	l = nil;
975 	p = buf;
976 	while(*p) {
977 		p = tkskip(p, " \t");
978 		buf = p;
979 		while(*p && *p != ' ' && *p != '\t')
980 			p++;
981 		if(*p != '\0')
982 			*p++ = '\0';
983 
984 		if(p == buf || buf[0] >= '0' && buf[0] <= '9') {
985 			tkfreename(l);
986 			return TkBadtg;
987 		}
988 		n = tkmkname(buf);
989 		if(n == nil) {
990 			tkfreename(l);
991 			return TkNomem;
992 		}
993 		n->link = l;
994 		l = n;
995 	}
996 	tkfreename(OPTION(place, TkName*, o->offset));
997 	OPTION(place, TkName*, o->offset) = l;
998 	return nil;
999 }
1000 
1001 static char*
1002 pfrac(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
1003 {
1004 	char *p, *e;
1005 	int i, n, d, *v;
1006 
1007 	*str = tkword(t, *str, buf, ebuf, nil);
1008 
1009 	v = &OPTION(place, int, o->offset);
1010 	n = (int)o->aux;
1011 	if(n == 0)
1012 		n = 1;
1013 	p = buf;
1014 	for(i = 0; i < n; i++) {
1015 		p = tkskip(p, " \t");
1016 		if(*p == '\0')
1017 			return TkOparg;
1018 		e = tkfracword(t, &p, &d, nil);
1019 		if (e != nil)
1020 			return e;
1021 		*v++ = d;
1022 	}
1023 	return nil;
1024 }
1025 
1026 /*
1027  * N.B. nnfrac only accepts aux==nil (can't deal with several items)
1028  */
1029 static char*
1030 pnnfrac(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
1031 {
1032 	int oldv;
1033 	char *e;
1034 
1035 	oldv = OPTION(place, int, o->offset);
1036 
1037 	e = pfrac(t, o, place, str, buf, ebuf);
1038 	if(e == nil && OPTION(place, int, o->offset) < 0) {
1039 		OPTION(place, int, o->offset) = oldv;
1040 		return TkBadvl;
1041 	}
1042 	return e;
1043 
1044 }
1045 
1046 typedef struct Tabspec {
1047 	int	dist;
1048 	int	just;
1049 	TkEnv	*env;
1050 } Tabspec;
1051 
1052 static char*
1053 ptabs(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
1054 {
1055 	char *e, *p, *eibuf;
1056 	TkOption opd, opj;
1057 	Tabspec tspec;
1058 	TkTtabstop *tabfirst, *tab, *tabprev;
1059 	char *ibuf;
1060 
1061 	ibuf = mallocz(Tkmaxitem, 0);
1062 	if(ibuf == nil)
1063 		return TkNomem;
1064 	eibuf = ibuf + Tkmaxitem;
1065 	tspec.env = OPTION(place, TkEnv*, AUXI(o->aux));
1066 	opd.offset = O(Tabspec, dist);
1067 	opd.aux = IAUX(O(Tabspec, env));
1068 	opj.offset = O(Tabspec, dist);
1069 	opj.aux = tktabjust;
1070 	tabprev = nil;
1071 	tabfirst = nil;
1072 
1073 	p = tkword(t, *str, buf, ebuf, nil);
1074 	if(*buf == '\0') {
1075 		free(ibuf);
1076 		return TkOparg;
1077 	}
1078 	*str = p;
1079 
1080 	p = buf;
1081 	while(*p != '\0') {
1082 		e = pdist(t, &opd, &tspec, &p, ibuf, eibuf);
1083 		if(e != nil) {
1084 			free(ibuf);
1085 			return e;
1086 		}
1087 
1088 		e = pstab(t, &opj, &tspec, &p, ibuf, eibuf);
1089 		if(e != nil)
1090 			tspec.just = Tkleft;
1091 
1092 		tab = malloc(sizeof(TkTtabstop));
1093 		if(tab == nil) {
1094 			free(ibuf);
1095 			return TkNomem;
1096 		}
1097 
1098 		tab->pos = tspec.dist;
1099 		tab->justify = tspec.just;
1100 		tab->next = nil;
1101 		if(tabfirst == nil)
1102 			tabfirst = tab;
1103 		else
1104 			tabprev->next = tab;
1105 		tabprev = tab;
1106 	}
1107 	free(ibuf);
1108 
1109 	tab = OPTION(place, TkTtabstop*, o->offset);
1110 	if(tab != nil)
1111 		free(tab);
1112 	OPTION(place, TkTtabstop*, o->offset) = tabfirst;
1113 	return nil;
1114 }
1115 
1116 char*
1117 tkxyparse(Tk* tk, char **parg, Point *p)
1118 {
1119 	char *buf;
1120 
1121 	buf = mallocz(Tkmaxitem, 0);
1122 	if(buf == nil)
1123 		return TkNomem;
1124 
1125 	*parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
1126 	if(*buf == '\0') {
1127 		free(buf);
1128 		return TkOparg;
1129 	}
1130 	p->x = atoi(buf);
1131 
1132 	*parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
1133 	if(*buf == '\0') {
1134 		free(buf);
1135 		return TkOparg;
1136 	}
1137 	p->y = atoi(buf);
1138 
1139 	free(buf);
1140 	return nil;
1141 }
1142 
1143 static char*
1144 plist(TkTop *t, TkOption *o, void *place, char **str, char *buf, char *ebuf)
1145 {
1146 	char *w, ***p, *wbuf, *ewbuf, **v, **nv;
1147 	int n, m, i, found;
1148 
1149 	*str = tkword(t, *str, buf, ebuf, nil);
1150 	n = strlen(buf) + 1;
1151 	wbuf = mallocz(n, 0);
1152 	if (wbuf == nil)
1153 		return TkNomem;		/* XXX should we free old values too? */
1154 	ewbuf = &wbuf[n];
1155 
1156 	p = &OPTION(place, char**, o->offset);
1157 	if (*p != nil){
1158 		for (v = *p; *v; v++)
1159 			free(*v);
1160 		free(*p);
1161 	}
1162 	n = 0;
1163 	m = 4;
1164 	w = buf;
1165 	v = malloc(m * sizeof(char*));
1166 	if (v == nil)
1167 		goto Error;
1168 	for (;;) {
1169 		w = tkword(t, w, wbuf, ewbuf, &found);
1170 		if (!found)
1171 			break;
1172 		if (n == m - 1) {
1173 			m += m/2;
1174 			nv = realloc(v, m * sizeof(char*));
1175 			if (nv == nil)
1176 				goto Error;
1177 			v = nv;
1178 		}
1179 		v[n] = strdup(wbuf);
1180 		if (v[n] == nil)
1181 			goto Error;
1182 		n++;
1183 	}
1184 	v[n++] = nil;
1185 	*p = realloc(v, n * sizeof(char*));
1186 	free(wbuf);
1187 	return nil;
1188 Error:
1189 	free(buf);
1190 	for (i = 0; i < n; i++)
1191 		free(v[i]);
1192 	free(v);
1193 	*p = nil;
1194 	return TkNomem;
1195 }
1196