compile.c revision 1.1
1/* $NetBSD: compile.c,v 1.1 2010/02/22 23:05:39 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.1 2010/02/22 23:05:39 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	*cap++ = 2; /* version */
239	le16enc(cap, len);
240	cap += sizeof(uint16_t);
241	memcpy(cap, tic->name, len);
242	cap += len;
243
244	le16enc(cap, alen);
245	cap += sizeof(uint16_t);
246	if (tic->alias != NULL) {
247		memcpy(cap, tic->alias, alen);
248		cap += alen;
249	}
250	le16enc(cap, dlen);
251	cap += sizeof(uint16_t);
252	if (tic->desc != NULL) {
253		memcpy(cap, tic->desc, dlen);
254		cap += dlen;
255	}
256
257	if (tic->flags.entries == 0) {
258		le16enc(cap, 0);
259		cap += sizeof(uint16_t);
260	} else {
261		le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t)));
262		cap += sizeof(uint16_t);
263		le16enc(cap, tic->flags.entries);
264		cap += sizeof(uint16_t);
265		memcpy(cap, tic->flags.buf, tic->flags.bufpos);
266		cap += tic->flags.bufpos;
267	}
268
269	if (tic->nums.entries == 0) {
270		le16enc(cap, 0);
271		cap += sizeof(uint16_t);
272	} else {
273		le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t)));
274		cap += sizeof(uint16_t);
275		le16enc(cap, tic->nums.entries);
276		cap += sizeof(uint16_t);
277		memcpy(cap, tic->nums.buf, tic->nums.bufpos);
278		cap += tic->nums.bufpos;
279	}
280
281	if (tic->strs.entries == 0) {
282		le16enc(cap, 0);
283		cap += sizeof(uint16_t);
284	} else {
285		le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t)));
286		cap += sizeof(uint16_t);
287		le16enc(cap, tic->strs.entries);
288		cap += sizeof(uint16_t);
289		memcpy(cap, tic->strs.buf, tic->strs.bufpos);
290		cap += tic->strs.bufpos;
291	}
292
293	if (tic->extras.entries == 0) {
294		le16enc(cap, 0);
295		cap += sizeof(uint16_t);
296	} else {
297		le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t)));
298		cap += sizeof(uint16_t);
299		le16enc(cap, tic->extras.entries);
300		cap += sizeof(uint16_t);
301		memcpy(cap, tic->extras.buf, tic->extras.bufpos);
302		cap += tic->extras.bufpos;
303	}
304
305	return cap - *buf;
306}
307
308static int
309encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
310    int flags)
311{
312	int slash, i, num;
313	char ch, *p, *s, last;
314
315	if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
316		return -1;
317	p = s = tbuf->buf + tbuf->bufpos;
318	slash = 0;
319	last = '\0';
320	/* Convert escape codes */
321	while ((ch = *str++) != '\0') {
322		if (slash == 0 && ch == '\\') {
323			slash = 1;
324			continue;
325		}
326		if (slash == 0) {
327			if (last != '%' && ch == '^') {
328				ch = *str++;
329				if (((unsigned char)ch) >= 128)
330					dowarn(flags,
331					    "%s: %s: illegal ^ character",
332					    term, cap);
333				if (ch == '\0')
334					break;
335				if (ch == '?')
336					ch = '\177';
337				else if ((ch &= 037) == 0)
338					ch = 128;
339			}
340			*p++ = ch;
341			last = ch;
342			continue;
343		}
344		slash = 0;
345		if (ch >= '0' && ch <= '7') {
346			num = ch - '0';
347			for (i = 0; i < 2; i++) {
348				if (*str < '0' || *str > '7') {
349					if (isdigit((unsigned char)*str))
350						dowarn(flags,
351						    "%s: %s: non octal"
352						    " digit", term, cap);
353					else
354						break;
355				}
356				num = num * 8 + *str++ - '0';
357			}
358			if (num == 0)
359				num = 0200;
360			*p++ = (char)num;
361			continue;
362		}
363		switch (ch) {
364		case 'a':
365			*p++ = '\a';
366			break;
367		case 'b':
368			*p++ = '\b';
369			break;
370		case 'e': /* FALLTHROUGH */
371		case 'E':
372			*p++ = '\033';
373			break;
374		case 'f':
375			*p++ = '\014';
376			break;
377		case 'l': /* FALLTHROUGH */
378		case 'n':
379			*p++ = '\n';
380			break;
381		case 'r':
382			*p++ = '\r';
383			break;
384		case 's':
385			*p++ = ' ';
386			break;
387		case 't':
388			*p++ = '\t';
389			break;
390		default:
391
392			/* We should warn here */
393		case '^':
394		case ',':
395		case ':':
396		case '|':
397			*p++ = ch;
398			break;
399		}
400		last = ch;
401	}
402	*p++ = '\0';
403	tbuf->bufpos += p - s;
404	return 0;
405}
406
407static char *
408get_token(char **cap)
409{
410	char *token;
411	int esc;
412
413	while (isspace((unsigned char)**cap))
414		(*cap)++;
415	if (**cap == '\0')
416		return NULL;
417
418	/* We can't use stresep(3) as ^ we need two escape chars */
419	esc = 0;
420	for (token = *cap;
421	     **cap != '\0' && (esc == 1 || **cap != ',');
422	     (*cap)++)
423	{
424		if (esc == 0) {
425			if (**cap == '\\' || **cap == '^')
426				esc = 1;
427		} else
428			esc = 0;
429	}
430
431	if (**cap != '\0')
432		*(*cap)++ = '\0';
433
434	return token;
435}
436
437TIC *
438_ti_compile(char *cap, int flags)
439{
440	char *token, *p, *e, *name, *desc, *alias;
441	signed char flag;
442	long num;
443	ssize_t ind;
444	size_t len;
445	TBUF buf;
446	TIC *tic;
447
448	_DIAGASSERT(cap != NULL);
449
450	name = get_token(&cap);
451	if (name == NULL) {
452		dowarn(flags, "no seperator found: %s", cap);
453		return NULL;
454	}
455	desc = strrchr(name, '|');
456	if (desc != NULL)
457		*desc++ = '\0';
458	alias = strchr(name, '|');
459	if (alias != NULL)
460		*alias++ = '\0';
461
462	tic = calloc(sizeof(*tic), 1);
463	if (tic == NULL)
464		return NULL;
465
466	buf.buf = NULL;
467	buf.buflen = 0;
468
469	tic->name = strdup(name);
470	if (tic->name == NULL)
471		goto error;
472	if (alias != NULL && flags & TIC_ALIAS) {
473		tic->alias = strdup(alias);
474		if (tic->alias == NULL)
475			goto error;
476	}
477	if (desc != NULL && flags & TIC_DESCRIPTION) {
478		tic->desc = strdup(desc);
479		if (tic->desc == NULL)
480			goto error;
481	}
482
483	for (token = get_token(&cap);
484	     token != NULL && *token != '\0';
485	     token = get_token(&cap))
486	{
487		/* Skip commented caps */
488		if (!(flags & TIC_COMMENT) && token[0] == '.')
489			continue;
490
491		/* Obsolete entries */
492		if (token[0] == 'O' && token[1] == 'T') {
493			if (!(flags & TIC_EXTRA))
494				continue;
495			token += 2;
496		}
497
498		/* str cap */
499		p = strchr(token, '=');
500		if (p != NULL) {
501			*p++ = '\0';
502			/* Don't use the string if we already have it */
503			ind = _ti_strindex(token);
504			if (ind != -1 &&
505			    _ti_find_cap(&tic->strs, 's', ind) != NULL)
506				continue;
507
508			/* Encode the string to our scratch buffer */
509			buf.bufpos = 0;
510			if (encode_string(tic->name, token,
511				&buf, p, flags) == -1)
512				goto error;
513			if (buf.bufpos > UINT16_T_MAX) {
514				dowarn(flags, "%s: %s: string is too long",
515				    tic->name, token);
516				continue;
517			}
518			if (!VALID_STRING(buf.buf)) {
519				dowarn(flags, "%s: %s: invalid string",
520				    tic->name, token);
521				continue;
522			}
523
524			if (ind == -1)
525				_ti_store_extra(tic, 1, token, 's', -1, -2,
526				    buf.buf, buf.bufpos, flags);
527			else {
528				if (!_ti_grow_tbuf(&tic->strs,
529					(sizeof(uint16_t) * 2) + buf.bufpos))
530					goto error;
531				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
532				tic->strs.bufpos += sizeof(uint16_t);
533				le16enc(tic->strs.buf + tic->strs.bufpos,
534				    buf.bufpos);
535				tic->strs.bufpos += sizeof(uint16_t);
536				memcpy(tic->strs.buf + tic->strs.bufpos,
537				    buf.buf, buf.bufpos);
538				tic->strs.bufpos += buf.bufpos;
539				tic->strs.entries++;
540			}
541			continue;
542		}
543
544		/* num cap */
545		p = strchr(token, '#');
546		if (p != NULL) {
547			*p++ = '\0';
548			/* Don't use the number if we already have it */
549			ind = _ti_numindex(token);
550			if (ind != -1 &&
551			    _ti_find_cap(&tic->nums, 'n', ind) != NULL)
552				continue;
553
554			num = strtol(p, &e, 0);
555			if (*e != '\0') {
556				dowarn(flags, "%s: %s: not a number",
557				    tic->name, token);
558				continue;
559			}
560			if (!VALID_NUMERIC(num)) {
561				dowarn(flags, "%s: %s: number out of range",
562				    tic->name, token);
563				continue;
564			}
565			if (ind == -1)
566				_ti_store_extra(tic, 1, token, 'n', -1,
567				    num, NULL, 0, flags);
568			else {
569				if (_ti_grow_tbuf(&tic->nums,
570					sizeof(uint16_t) * 2) == NULL)
571					goto error;
572				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
573				tic->nums.bufpos += sizeof(uint16_t);
574				le16enc(tic->nums.buf + tic->nums.bufpos, num);
575				tic->nums.bufpos += sizeof(uint16_t);
576				tic->nums.entries++;
577			}
578			continue;
579		}
580
581		flag = 1;
582		len = strlen(token) - 1;
583		if (token[len] == '@') {
584			flag = CANCELLED_BOOLEAN;
585			token[len] = '\0';
586		}
587		ind = _ti_flagindex(token);
588		if (ind == -1 && flag == CANCELLED_BOOLEAN) {
589			if ((ind = _ti_numindex(token)) != -1) {
590				if (_ti_find_cap(&tic->nums, 'n', ind) != NULL)
591					continue;
592				if (_ti_grow_tbuf(&tic->nums,
593					sizeof(uint16_t) * 2) == NULL)
594					goto error;
595				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
596				tic->nums.bufpos += sizeof(uint16_t);
597				le16enc(tic->nums.buf + tic->nums.bufpos,
598					CANCELLED_NUMERIC);
599				tic->nums.bufpos += sizeof(uint16_t);
600				tic->nums.entries++;
601				continue;
602			} else if ((ind = _ti_strindex(token)) != -1) {
603				if (_ti_find_cap(&tic->strs, 's', ind) != NULL)
604					continue;
605				if (_ti_grow_tbuf(&tic->strs,
606					(sizeof(uint16_t) * 2) + 1) == NULL)
607					goto error;
608				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
609				tic->strs.bufpos += sizeof(uint16_t);
610				le16enc(tic->strs.buf + tic->strs.bufpos, 0);
611				tic->strs.bufpos += sizeof(uint16_t);
612				tic->strs.entries++;
613				continue;
614			}
615		}
616		if (ind == -1)
617			_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 0,
618			    flags);
619		else if (_ti_find_cap(&tic->flags, 'f', ind) == NULL) {
620			if (_ti_grow_tbuf(&tic->flags, sizeof(uint16_t) + 1)
621			    == NULL)
622				goto error;
623			le16enc(tic->flags.buf + tic->flags.bufpos, ind);
624			tic->flags.bufpos += sizeof(uint16_t);
625			tic->flags.buf[tic->flags.bufpos++] = flag;
626			tic->flags.entries++;
627		}
628	}
629
630	free(buf.buf);
631	return tic;
632
633error:
634	free(buf.buf);
635	_ti_freetic(tic);
636	return NULL;
637}
638
639void
640_ti_freetic(TIC *tic)
641{
642
643	if (tic != NULL) {
644		free(tic->name);
645		free(tic->alias);
646		free(tic->desc);
647		free(tic->flags.buf);
648		free(tic->nums.buf);
649		free(tic->strs.buf);
650		free(tic);
651	}
652}
653