compile.c revision 1.2
1/* $NetBSD: compile.c,v 1.2 2010/02/25 23:44:02 roy Exp $ */
2
3/*
4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Roy Marples.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#if HAVE_NBTOOL_CONFIG_H
31#include "nbtool_config.h"
32#endif
33
34#include <sys/cdefs.h>
35__RCSID("$NetBSD: compile.c,v 1.2 2010/02/25 23:44:02 roy Exp $");
36
37#include <assert.h>
38#include <ctype.h>
39#include <err.h>
40#include <errno.h>
41#include <limits.h>
42#include <stdarg.h>
43#include <stdlib.h>
44#include <stdint.h>
45#include <stdio.h>
46#include <string.h>
47#include <term_private.h>
48#include <term.h>
49
50static void __attribute__((__format__(__printf__, 2, 3)))
51dowarn(int flags, const char *fmt, ...)
52{
53	va_list va;
54
55	errno = EINVAL;
56	if (flags & TIC_WARNING) {
57		va_start(va, fmt);
58		vwarnx(fmt, va);
59		va_end(va);
60	}
61}
62
63char *
64_ti_grow_tbuf(TBUF *tbuf, size_t len)
65{
66	char *buf;
67	size_t l;
68
69	_DIAGASSERT(tbuf != NULL);
70
71	l = tbuf->bufpos + len;
72	if (l > tbuf->buflen) {
73		if (tbuf->bufpos == 0)
74			buf = malloc(l);
75		else
76			buf = realloc(tbuf->buf, l);
77		if (buf == NULL)
78			return NULL;
79		tbuf->buf = buf;
80		tbuf->buflen = l;
81	}
82	return tbuf->buf;
83}
84
85char *
86_ti_find_cap(TBUF *tbuf, char type, short ind)
87{
88	size_t n;
89	short num;
90	char *cap;
91
92	_DIAGASSERT(tbuf != NULL);
93
94	cap = tbuf->buf;
95	for (n = tbuf->entries; n > 0; n--) {
96		num = le16dec(cap);
97		cap += sizeof(uint16_t);
98		if (num == ind)
99			return cap;
100		switch (type) {
101		case 'f':
102			cap++;
103			break;
104		case 'n':
105			cap += sizeof(uint16_t);
106			break;
107		case 's':
108			num = le16dec(cap);
109			cap += sizeof(uint16_t);
110			cap += num;
111			break;
112		}
113	}
114
115	errno = ESRCH;
116	return NULL;
117}
118
119char *
120_ti_find_extra(TBUF *tbuf, const char *code)
121{
122	size_t n;
123	short num;
124	char *cap;
125
126	_DIAGASSERT(tbuf != NULL);
127	_DIAGASSERT(code != NULL);
128
129	cap = tbuf->buf;
130	for (n = tbuf->entries; n > 0; n--) {
131		num = le16dec(cap);
132		cap += sizeof(uint16_t);
133		if (strcmp(cap, code) == 0)
134			return cap + num;
135		cap += num;
136		switch (*cap++) {
137		case 'f':
138			cap++;
139			break;
140		case 'n':
141			cap += sizeof(uint16_t);
142			break;
143		case 's':
144			num = le16dec(cap);
145			cap += sizeof(uint16_t);
146			cap += num;
147			break;
148		}
149	}
150
151	errno = ESRCH;
152	return NULL;
153}
154
155size_t
156_ti_store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num,
157    char *str, size_t strl, int flags)
158{
159	size_t l;
160
161	_DIAGASSERT(tic != NULL);
162
163	if (strcmp(id, "use") != 0) {
164		if (_ti_find_extra(&tic->extras, id) != NULL)
165			return 0;
166		if (!(flags & TIC_EXTRA)) {
167			if (wrn != 0)
168				dowarn(flags, "%s: %s: unknown capability",
169				    tic->name, id);
170			return 0;
171		}
172	}
173
174	l = strlen(id) + 1;
175	if (l > UINT16_T_MAX) {
176		dowarn(flags, "%s: %s: cap name is too long", tic->name, id);
177		return 0;
178	}
179
180	if (!_ti_grow_tbuf(&tic->extras,
181		l + strl + (sizeof(uint16_t) * 2) + 1))
182		return 0;
183	le16enc(tic->extras.buf + tic->extras.bufpos, l);
184	tic->extras.bufpos += sizeof(uint16_t);
185     	memcpy(tic->extras.buf + tic->extras.bufpos, id, l);
186	tic->extras.bufpos += l;
187	tic->extras.buf[tic->extras.bufpos++] = type;
188	switch (type) {
189	case 'f':
190		tic->extras.buf[tic->extras.bufpos++] = flag;
191		break;
192	case 'n':
193		le16enc(tic->extras.buf + tic->extras.bufpos, num);
194		tic->extras.bufpos += sizeof(uint16_t);
195		break;
196	case 's':
197		le16enc(tic->extras.buf + tic->extras.bufpos, strl);
198		tic->extras.bufpos += sizeof(uint16_t);
199		memcpy(tic->extras.buf + tic->extras.bufpos, str, strl);
200		tic->extras.bufpos += strl;
201		break;
202	}
203	tic->extras.entries++;
204	return 1;
205}
206
207ssize_t
208_ti_flatten(uint8_t **buf, const TIC *tic)
209{
210	size_t buflen, len, alen, dlen;
211	uint8_t *cap;
212
213	_DIAGASSERT(buf != NULL);
214	_DIAGASSERT(tic != NULL);
215
216	len = strlen(tic->name) + 1;
217	if (tic->alias == NULL)
218		alen = 0;
219	else
220		alen = strlen(tic->alias) + 1;
221	if (tic->desc == NULL)
222		dlen = 0;
223	else
224		dlen = strlen(tic->desc) + 1;
225	buflen = sizeof(char) +
226	    sizeof(uint16_t) + len +
227	    sizeof(uint16_t) + alen +
228	    sizeof(uint16_t) + dlen +
229	    (sizeof(uint16_t) * 2) + tic->flags.bufpos +
230	    (sizeof(uint16_t) * 2) + tic->nums.bufpos +
231	    (sizeof(uint16_t) * 2) + tic->strs.bufpos +
232	    (sizeof(uint16_t) * 2) + tic->extras.bufpos;
233	*buf = malloc(buflen);
234	if (*buf == NULL)
235		return -1;
236
237	cap = *buf;
238	if (alen == 0 && dlen == 0 && tic->flags.bufpos == 0 &&
239	    tic->nums.bufpos == 0 && tic->strs.bufpos == 0 &&
240	    tic->extras.bufpos == 0)
241		*cap++ = 0; /* alias */
242	else
243		*cap++ = 2; /* version */
244	le16enc(cap, len);
245	cap += sizeof(uint16_t);
246	memcpy(cap, tic->name, len);
247	cap += len;
248
249	le16enc(cap, alen);
250	cap += sizeof(uint16_t);
251	if (tic->alias != NULL) {
252		memcpy(cap, tic->alias, alen);
253		cap += alen;
254	}
255	le16enc(cap, dlen);
256	cap += sizeof(uint16_t);
257	if (tic->desc != NULL) {
258		memcpy(cap, tic->desc, dlen);
259		cap += dlen;
260	}
261
262	if (tic->flags.entries == 0) {
263		le16enc(cap, 0);
264		cap += sizeof(uint16_t);
265	} else {
266		le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t)));
267		cap += sizeof(uint16_t);
268		le16enc(cap, tic->flags.entries);
269		cap += sizeof(uint16_t);
270		memcpy(cap, tic->flags.buf, tic->flags.bufpos);
271		cap += tic->flags.bufpos;
272	}
273
274	if (tic->nums.entries == 0) {
275		le16enc(cap, 0);
276		cap += sizeof(uint16_t);
277	} else {
278		le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t)));
279		cap += sizeof(uint16_t);
280		le16enc(cap, tic->nums.entries);
281		cap += sizeof(uint16_t);
282		memcpy(cap, tic->nums.buf, tic->nums.bufpos);
283		cap += tic->nums.bufpos;
284	}
285
286	if (tic->strs.entries == 0) {
287		le16enc(cap, 0);
288		cap += sizeof(uint16_t);
289	} else {
290		le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t)));
291		cap += sizeof(uint16_t);
292		le16enc(cap, tic->strs.entries);
293		cap += sizeof(uint16_t);
294		memcpy(cap, tic->strs.buf, tic->strs.bufpos);
295		cap += tic->strs.bufpos;
296	}
297
298	if (tic->extras.entries == 0) {
299		le16enc(cap, 0);
300		cap += sizeof(uint16_t);
301	} else {
302		le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t)));
303		cap += sizeof(uint16_t);
304		le16enc(cap, tic->extras.entries);
305		cap += sizeof(uint16_t);
306		memcpy(cap, tic->extras.buf, tic->extras.bufpos);
307		cap += tic->extras.bufpos;
308	}
309
310	return cap - *buf;
311}
312
313static int
314encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
315    int flags)
316{
317	int slash, i, num;
318	char ch, *p, *s, last;
319
320	if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
321		return -1;
322	p = s = tbuf->buf + tbuf->bufpos;
323	slash = 0;
324	last = '\0';
325	/* Convert escape codes */
326	while ((ch = *str++) != '\0') {
327		if (slash == 0 && ch == '\\') {
328			slash = 1;
329			continue;
330		}
331		if (slash == 0) {
332			if (last != '%' && ch == '^') {
333				ch = *str++;
334				if (((unsigned char)ch) >= 128)
335					dowarn(flags,
336					    "%s: %s: illegal ^ character",
337					    term, cap);
338				if (ch == '\0')
339					break;
340				if (ch == '?')
341					ch = '\177';
342				else if ((ch &= 037) == 0)
343					ch = 128;
344			}
345			*p++ = ch;
346			last = ch;
347			continue;
348		}
349		slash = 0;
350		if (ch >= '0' && ch <= '7') {
351			num = ch - '0';
352			for (i = 0; i < 2; i++) {
353				if (*str < '0' || *str > '7') {
354					if (isdigit((unsigned char)*str))
355						dowarn(flags,
356						    "%s: %s: non octal"
357						    " digit", term, cap);
358					else
359						break;
360				}
361				num = num * 8 + *str++ - '0';
362			}
363			if (num == 0)
364				num = 0200;
365			*p++ = (char)num;
366			continue;
367		}
368		switch (ch) {
369		case 'a':
370			*p++ = '\a';
371			break;
372		case 'b':
373			*p++ = '\b';
374			break;
375		case 'e': /* FALLTHROUGH */
376		case 'E':
377			*p++ = '\033';
378			break;
379		case 'f':
380			*p++ = '\014';
381			break;
382		case 'l': /* FALLTHROUGH */
383		case 'n':
384			*p++ = '\n';
385			break;
386		case 'r':
387			*p++ = '\r';
388			break;
389		case 's':
390			*p++ = ' ';
391			break;
392		case 't':
393			*p++ = '\t';
394			break;
395		default:
396
397			/* We should warn here */
398		case '^':
399		case ',':
400		case ':':
401		case '|':
402			*p++ = ch;
403			break;
404		}
405		last = ch;
406	}
407	*p++ = '\0';
408	tbuf->bufpos += p - s;
409	return 0;
410}
411
412static char *
413get_token(char **cap)
414{
415	char *token;
416	int esc;
417
418	while (isspace((unsigned char)**cap))
419		(*cap)++;
420	if (**cap == '\0')
421		return NULL;
422
423	/* We can't use stresep(3) as ^ we need two escape chars */
424	esc = 0;
425	for (token = *cap;
426	     **cap != '\0' && (esc == 1 || **cap != ',');
427	     (*cap)++)
428	{
429		if (esc == 0) {
430			if (**cap == '\\' || **cap == '^')
431				esc = 1;
432		} else
433			esc = 0;
434	}
435
436	if (**cap != '\0')
437		*(*cap)++ = '\0';
438
439	return token;
440}
441
442TIC *
443_ti_compile(char *cap, int flags)
444{
445	char *token, *p, *e, *name, *desc, *alias;
446	signed char flag;
447	long num;
448	ssize_t ind;
449	size_t len;
450	TBUF buf;
451	TIC *tic;
452
453	_DIAGASSERT(cap != NULL);
454
455	name = get_token(&cap);
456	if (name == NULL) {
457		dowarn(flags, "no seperator found: %s", cap);
458		return NULL;
459	}
460	desc = strrchr(name, '|');
461	if (desc != NULL)
462		*desc++ = '\0';
463	alias = strchr(name, '|');
464	if (alias != NULL)
465		*alias++ = '\0';
466
467	tic = calloc(sizeof(*tic), 1);
468	if (tic == NULL)
469		return NULL;
470
471	buf.buf = NULL;
472	buf.buflen = 0;
473
474	tic->name = strdup(name);
475	if (tic->name == NULL)
476		goto error;
477	if (alias != NULL && flags & TIC_ALIAS) {
478		tic->alias = strdup(alias);
479		if (tic->alias == NULL)
480			goto error;
481	}
482	if (desc != NULL && flags & TIC_DESCRIPTION) {
483		tic->desc = strdup(desc);
484		if (tic->desc == NULL)
485			goto error;
486	}
487
488	for (token = get_token(&cap);
489	     token != NULL && *token != '\0';
490	     token = get_token(&cap))
491	{
492		/* Skip commented caps */
493		if (!(flags & TIC_COMMENT) && token[0] == '.')
494			continue;
495
496		/* Obsolete entries */
497		if (token[0] == 'O' && token[1] == 'T') {
498			if (!(flags & TIC_EXTRA))
499				continue;
500			token += 2;
501		}
502
503		/* str cap */
504		p = strchr(token, '=');
505		if (p != NULL) {
506			*p++ = '\0';
507			/* Don't use the string if we already have it */
508			ind = _ti_strindex(token);
509			if (ind != -1 &&
510			    _ti_find_cap(&tic->strs, 's', ind) != NULL)
511				continue;
512
513			/* Encode the string to our scratch buffer */
514			buf.bufpos = 0;
515			if (encode_string(tic->name, token,
516				&buf, p, flags) == -1)
517				goto error;
518			if (buf.bufpos > UINT16_T_MAX) {
519				dowarn(flags, "%s: %s: string is too long",
520				    tic->name, token);
521				continue;
522			}
523			if (!VALID_STRING(buf.buf)) {
524				dowarn(flags, "%s: %s: invalid string",
525				    tic->name, token);
526				continue;
527			}
528
529			if (ind == -1)
530				_ti_store_extra(tic, 1, token, 's', -1, -2,
531				    buf.buf, buf.bufpos, flags);
532			else {
533				if (!_ti_grow_tbuf(&tic->strs,
534					(sizeof(uint16_t) * 2) + buf.bufpos))
535					goto error;
536				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
537				tic->strs.bufpos += sizeof(uint16_t);
538				le16enc(tic->strs.buf + tic->strs.bufpos,
539				    buf.bufpos);
540				tic->strs.bufpos += sizeof(uint16_t);
541				memcpy(tic->strs.buf + tic->strs.bufpos,
542				    buf.buf, buf.bufpos);
543				tic->strs.bufpos += buf.bufpos;
544				tic->strs.entries++;
545			}
546			continue;
547		}
548
549		/* num cap */
550		p = strchr(token, '#');
551		if (p != NULL) {
552			*p++ = '\0';
553			/* Don't use the number if we already have it */
554			ind = _ti_numindex(token);
555			if (ind != -1 &&
556			    _ti_find_cap(&tic->nums, 'n', ind) != NULL)
557				continue;
558
559			num = strtol(p, &e, 0);
560			if (*e != '\0') {
561				dowarn(flags, "%s: %s: not a number",
562				    tic->name, token);
563				continue;
564			}
565			if (!VALID_NUMERIC(num)) {
566				dowarn(flags, "%s: %s: number out of range",
567				    tic->name, token);
568				continue;
569			}
570			if (ind == -1)
571				_ti_store_extra(tic, 1, token, 'n', -1,
572				    num, NULL, 0, flags);
573			else {
574				if (_ti_grow_tbuf(&tic->nums,
575					sizeof(uint16_t) * 2) == NULL)
576					goto error;
577				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
578				tic->nums.bufpos += sizeof(uint16_t);
579				le16enc(tic->nums.buf + tic->nums.bufpos, num);
580				tic->nums.bufpos += sizeof(uint16_t);
581				tic->nums.entries++;
582			}
583			continue;
584		}
585
586		flag = 1;
587		len = strlen(token) - 1;
588		if (token[len] == '@') {
589			flag = CANCELLED_BOOLEAN;
590			token[len] = '\0';
591		}
592		ind = _ti_flagindex(token);
593		if (ind == -1 && flag == CANCELLED_BOOLEAN) {
594			if ((ind = _ti_numindex(token)) != -1) {
595				if (_ti_find_cap(&tic->nums, 'n', ind) != NULL)
596					continue;
597				if (_ti_grow_tbuf(&tic->nums,
598					sizeof(uint16_t) * 2) == NULL)
599					goto error;
600				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
601				tic->nums.bufpos += sizeof(uint16_t);
602				le16enc(tic->nums.buf + tic->nums.bufpos,
603					CANCELLED_NUMERIC);
604				tic->nums.bufpos += sizeof(uint16_t);
605				tic->nums.entries++;
606				continue;
607			} else if ((ind = _ti_strindex(token)) != -1) {
608				if (_ti_find_cap(&tic->strs, 's', ind) != NULL)
609					continue;
610				if (_ti_grow_tbuf(&tic->strs,
611					(sizeof(uint16_t) * 2) + 1) == NULL)
612					goto error;
613				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
614				tic->strs.bufpos += sizeof(uint16_t);
615				le16enc(tic->strs.buf + tic->strs.bufpos, 0);
616				tic->strs.bufpos += sizeof(uint16_t);
617				tic->strs.entries++;
618				continue;
619			}
620		}
621		if (ind == -1)
622			_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 0,
623			    flags);
624		else if (_ti_find_cap(&tic->flags, 'f', ind) == NULL) {
625			if (_ti_grow_tbuf(&tic->flags, sizeof(uint16_t) + 1)
626			    == NULL)
627				goto error;
628			le16enc(tic->flags.buf + tic->flags.bufpos, ind);
629			tic->flags.bufpos += sizeof(uint16_t);
630			tic->flags.buf[tic->flags.bufpos++] = flag;
631			tic->flags.entries++;
632		}
633	}
634
635	free(buf.buf);
636	return tic;
637
638error:
639	free(buf.buf);
640	_ti_freetic(tic);
641	return NULL;
642}
643
644void
645_ti_freetic(TIC *tic)
646{
647
648	if (tic != NULL) {
649		free(tic->name);
650		free(tic->alias);
651		free(tic->desc);
652		free(tic->flags.buf);
653		free(tic->nums.buf);
654		free(tic->strs.buf);
655		free(tic);
656	}
657}
658