compile.c revision 1.24
1/* $NetBSD: compile.c,v 1.24 2020/03/30 02:08:11 roy Exp $ */
2
3/*
4 * Copyright (c) 2009, 2010, 2011, 2020 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.24 2020/03/30 02:08:11 roy Exp $");
36
37#if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
38#include <sys/endian.h>
39#endif
40
41#include <assert.h>
42#include <ctype.h>
43#include <err.h>
44#include <errno.h>
45#include <limits.h>
46#include <stdarg.h>
47#include <stdlib.h>
48#include <stdint.h>
49#include <stdio.h>
50#include <string.h>
51#include <term_private.h>
52#include <term.h>
53
54static void __printflike(2, 3)
55dowarn(int flags, const char *fmt, ...)
56{
57	va_list va;
58
59	errno = EINVAL;
60	if (flags & TIC_WARNING) {
61		va_start(va, fmt);
62		vwarnx(fmt, va);
63		va_end(va);
64	}
65}
66
67int
68_ti_promote(TIC *tic)
69{
70	char *obuf, type, flag, *buf, *delim, *name, *nbuf;
71	const char *cap, *code, *str;
72	size_t n, entries, strl;
73	uint16_t ind;
74	int num, ortype, error = 0;
75
76	ortype = tic->rtype;
77	tic->rtype = TERMINFO_RTYPE;
78	obuf = tic->name;
79	tic->name = _ti_getname(tic->rtype, tic->name);
80	if (tic->name == NULL) {
81		warn("_ti_getname");
82		tic->name = obuf;
83		return -1;
84	}
85	free(obuf);
86
87	n = 0;
88	obuf = buf = tic->alias;
89	tic->alias = NULL;
90	while (buf != NULL) {
91		delim = strchr(buf, '|');
92		if (delim != NULL)
93			*delim++ = '\0';
94		name = _ti_getname(tic->rtype, buf);
95		strl = strlen(name) + 1;
96		nbuf = realloc(tic->alias, n + strl);
97		if (nbuf == NULL) {
98			free(name);
99			return -1;
100		}
101		tic->alias = nbuf;
102		memcpy(tic->alias + n, name, strl);
103		n += strl;
104		free(name);
105		buf = delim;
106	}
107	free(obuf);
108
109	obuf = tic->nums.buf;
110	cap = obuf;
111	entries = tic->nums.entries;
112	tic->nums.buf = NULL;
113	tic->nums.entries = tic->nums.buflen = tic->nums.bufpos = 0;
114	for (n = entries; n > 0; n--) {
115		ind = _ti_decode_16(&cap);
116		num = _ti_decode_num(&cap, ortype);
117		if (VALID_NUMERIC(num) &&
118		    !_ti_encode_buf_id_num(&tic->nums, ind, num,
119		    _ti_numsize(tic)))
120		{
121			warn("promote num");
122			error = -1;
123			break;
124		}
125	}
126	free(obuf);
127
128	obuf = tic->extras.buf;
129	cap = obuf;
130	entries = tic->extras.entries;
131	tic->extras.buf = NULL;
132	tic->extras.entries = tic->extras.buflen = tic->extras.bufpos = 0;
133	for (n = entries; n > 0; n--) {
134		num = _ti_decode_16(&cap);
135		flag = 0; /* satisfy gcc, won't be used for non flag types */
136		str = NULL; /* satisfy gcc, won't be used as strl is 0 */
137		strl = 0;
138		code = cap;
139		cap += num;
140		type = *cap++;
141		switch (type) {
142		case 'f':
143			flag = *cap++;
144			break;
145		case 'n':
146			num = _ti_decode_num(&cap, ortype);
147			break;
148		case 's':
149			strl = _ti_decode_16(&cap);
150			str = cap;
151			cap += strl;
152			break;
153		default:
154			errno = EINVAL;
155			break;
156		}
157		if (!_ti_store_extra(tic, 0, code, type, flag, num,
158		    str, strl, TIC_EXTRA))
159		{
160			error = -1;
161			break;
162		}
163	}
164	free(obuf);
165
166	return error;
167}
168
169char *
170_ti_grow_tbuf(TBUF *tbuf, size_t len)
171{
172	char *buf;
173	size_t l;
174
175	_DIAGASSERT(tbuf != NULL);
176
177	l = tbuf->bufpos + len;
178	if (l > tbuf->buflen) {
179		if (tbuf->buflen == 0)
180			buf = malloc(l);
181		else
182			buf = realloc(tbuf->buf, l);
183		if (buf == NULL)
184			return NULL;
185		tbuf->buf = buf;
186		tbuf->buflen = l;
187	}
188	return tbuf->buf;
189}
190
191const char *
192_ti_find_cap(TIC *tic, TBUF *tbuf, char type, short ind)
193{
194	size_t n;
195	uint16_t num;
196	const char *cap;
197
198	_DIAGASSERT(tbuf != NULL);
199
200	cap = tbuf->buf;
201	for (n = tbuf->entries; n > 0; n--) {
202		num = _ti_decode_16(&cap);
203		if ((short)num == ind)
204			return cap;
205		switch (type) {
206		case 'f':
207			cap++;
208			break;
209		case 'n':
210			cap += _ti_numsize(tic);
211			break;
212		case 's':
213			num = _ti_decode_16(&cap);
214			cap += num;
215			break;
216		}
217	}
218
219	errno = ESRCH;
220	return NULL;
221}
222
223const char *
224_ti_find_extra(TIC *tic, TBUF *tbuf, const char *code)
225{
226	size_t n;
227	uint16_t num;
228	const char *cap;
229
230	_DIAGASSERT(tbuf != NULL);
231	_DIAGASSERT(code != NULL);
232
233	cap = tbuf->buf;
234	for (n = tbuf->entries; n > 0; n--) {
235		num = _ti_decode_16(&cap);
236		if (strcmp(cap, code) == 0)
237			return cap + num;
238		cap += num;
239		switch (*cap++) {
240		case 'f':
241			cap++;
242			break;
243		case 'n':
244			cap += _ti_numsize(tic);
245			break;
246		case 's':
247			num = _ti_decode_16(&cap);
248			cap += num;
249			break;
250		}
251	}
252
253	errno = ESRCH;
254	return NULL;
255}
256
257char *
258_ti_getname(int rtype, const char *orig)
259{
260	const char *delim;
261	char *name;
262	const char *verstr;
263	size_t diff, vlen;
264
265	switch (rtype) {
266	case TERMINFO_RTYPE:
267		verstr = TERMINFO_VDELIMSTR "v3";
268		break;
269	case TERMINFO_RTYPE_O1:
270		verstr = "";
271		break;
272	default:
273		errno = EINVAL;
274		return NULL;
275	}
276
277	delim = orig;
278	while (*delim != '\0' && *delim != TERMINFO_VDELIM)
279		delim++;
280	diff = delim - orig;
281	vlen = strlen(verstr);
282	name = malloc(diff + vlen + 1);
283	if (name == NULL)
284		return NULL;
285
286	memcpy(name, orig, diff);
287	memcpy(name + diff, verstr, vlen + 1);
288	return name;
289}
290
291size_t
292_ti_store_extra(TIC *tic, int wrn, const char *id, char type, char flag,
293    int num, const char *str, size_t strl, int flags)
294{
295	size_t l, capl;
296
297	_DIAGASSERT(tic != NULL);
298
299	if (strcmp(id, "use") != 0) {
300		if (_ti_find_extra(tic, &tic->extras, id) != NULL)
301			return 0;
302		if (!(flags & TIC_EXTRA)) {
303			if (wrn != 0)
304				dowarn(flags, "%s: %s: unknown capability",
305				    tic->name, id);
306			return 0;
307		}
308	}
309
310	l = strlen(id) + 1;
311	if (l > UINT16_MAX) {
312		dowarn(flags, "%s: %s: cap name is too long", tic->name, id);
313		return 0;
314	}
315
316	capl = sizeof(uint16_t) + l + 1;
317	switch (type) {
318	case 'f':
319		capl++;
320		break;
321	case 'n':
322		capl += _ti_numsize(tic);
323		break;
324	case 's':
325		capl += sizeof(uint16_t) + strl;
326		break;
327	}
328
329	if (!_ti_grow_tbuf(&tic->extras, capl))
330		return 0;
331	_ti_encode_buf_count_str(&tic->extras, id, l);
332	tic->extras.buf[tic->extras.bufpos++] = type;
333	switch (type) {
334	case 'f':
335		tic->extras.buf[tic->extras.bufpos++] = flag;
336		break;
337	case 'n':
338		_ti_encode_buf_num(&tic->extras, num, tic->rtype);
339		break;
340	case 's':
341		_ti_encode_buf_count_str(&tic->extras, str, strl);
342		break;
343	}
344	tic->extras.entries++;
345	return 1;
346}
347
348static void
349_ti_encode_buf(char **cap, const TBUF *buf)
350{
351	if (buf->entries == 0) {
352		_ti_encode_16(cap, 0);
353	} else {
354		_ti_encode_16(cap, buf->bufpos + sizeof(uint16_t));
355		_ti_encode_16(cap, buf->entries);
356		_ti_encode_str(cap, buf->buf, buf->bufpos);
357	}
358}
359
360ssize_t
361_ti_flatten(uint8_t **buf, const TIC *tic)
362{
363	size_t buflen, len, alen, dlen;
364	char *cap;
365
366	_DIAGASSERT(buf != NULL);
367	_DIAGASSERT(tic != NULL);
368
369	len = strlen(tic->name) + 1;
370	if (tic->alias == NULL)
371		alen = 0;
372	else
373		alen = strlen(tic->alias) + 1;
374	if (tic->desc == NULL)
375		dlen = 0;
376	else
377		dlen = strlen(tic->desc) + 1;
378
379	buflen = sizeof(char) +
380	    sizeof(uint16_t) + len +
381	    sizeof(uint16_t) + alen +
382	    sizeof(uint16_t) + dlen +
383	    (sizeof(uint16_t) * 2) + tic->flags.bufpos +
384	    (sizeof(uint16_t) * 2) + tic->nums.bufpos +
385	    (sizeof(uint16_t) * 2) + tic->strs.bufpos +
386	    (sizeof(uint16_t) * 2) + tic->extras.bufpos;
387
388	*buf = malloc(buflen);
389	if (*buf == NULL)
390		return -1;
391
392	cap = (char *)*buf;
393	*cap++ = tic->rtype;
394
395	_ti_encode_count_str(&cap, tic->name, len);
396	_ti_encode_count_str(&cap, tic->alias, alen);
397	_ti_encode_count_str(&cap, tic->desc, dlen);
398
399	_ti_encode_buf(&cap, &tic->flags);
400
401	_ti_encode_buf(&cap, &tic->nums);
402	_ti_encode_buf(&cap, &tic->strs);
403	_ti_encode_buf(&cap, &tic->extras);
404
405	return (uint8_t *)cap - *buf;
406}
407
408static int
409encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
410    int flags)
411{
412	int slash, i, num;
413	char ch, *p, *s, last;
414
415	if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
416		return -1;
417	p = s = tbuf->buf + tbuf->bufpos;
418	slash = 0;
419	last = '\0';
420	/* Convert escape codes */
421	while ((ch = *str++) != '\0') {
422		if (ch == '\n') {
423			/* Following a newline, strip leading whitespace from
424			 * capability strings. */
425			while (isspace((unsigned char)*str))
426				str++;
427			continue;
428		}
429		if (slash == 0 && ch == '\\') {
430			slash = 1;
431			continue;
432		}
433		if (slash == 0) {
434			if (last != '%' && ch == '^') {
435				ch = *str++;
436				if (((unsigned char)ch) >= 128)
437					dowarn(flags,
438					    "%s: %s: illegal ^ character",
439					    term, cap);
440				if (ch == '\0')
441					break;
442				if (ch == '?')
443					ch = '\177';
444				else if ((ch &= 037) == 0)
445					ch = (char)128;
446			} else if (!isprint((unsigned char)ch))
447				dowarn(flags,
448				    "%s: %s: unprintable character",
449				    term, cap);
450			*p++ = ch;
451			last = ch;
452			continue;
453		}
454		slash = 0;
455		if (ch >= '0' && ch <= '7') {
456			num = ch - '0';
457			for (i = 0; i < 2; i++) {
458				if (*str < '0' || *str > '7') {
459					if (isdigit((unsigned char)*str))
460						dowarn(flags,
461						    "%s: %s: non octal"
462						    " digit", term, cap);
463					else
464						break;
465				}
466				num = num * 8 + *str++ - '0';
467			}
468			if (num == 0)
469				num = 0200;
470			*p++ = (char)num;
471			continue;
472		}
473		switch (ch) {
474		case 'a':
475			*p++ = '\a';
476			break;
477		case 'b':
478			*p++ = '\b';
479			break;
480		case 'e': /* FALLTHROUGH */
481		case 'E':
482			*p++ = '\033';
483			break;
484		case 'f':
485			*p++ = '\014';
486			break;
487		case 'l': /* FALLTHROUGH */
488		case 'n':
489			*p++ = '\n';
490			break;
491		case 'r':
492			*p++ = '\r';
493			break;
494		case 's':
495			*p++ = ' ';
496			break;
497		case 't':
498			*p++ = '\t';
499			break;
500		default:
501			/* We should warn here */
502		case '^':
503		case ',':
504		case ':':
505		case '|':
506			*p++ = ch;
507			break;
508		}
509		last = ch;
510	}
511	*p++ = '\0';
512	tbuf->bufpos += (size_t)(p - s);
513	return 0;
514}
515
516char *
517_ti_get_token(char **cap, char sep)
518{
519	char esc, *token;
520
521	while (isspace((unsigned char)**cap))
522		(*cap)++;
523	if (**cap == '\0')
524		return NULL;
525
526	/* We can't use stresep(3) as ^ we need two escape chars */
527	esc = '\0';
528	for (token = *cap;
529	     **cap != '\0' && (esc != '\0' || **cap != sep);
530	     (*cap)++)
531	{
532		if (esc == '\0') {
533			if (**cap == '\\' || **cap == '^')
534				esc = **cap;
535		} else {
536			/* termcap /E/ is valid */
537			if (sep == ':' && esc == '\\' && **cap == 'E')
538				esc = 'x';
539			else
540				esc = '\0';
541		}
542	}
543
544	if (**cap != '\0')
545		*(*cap)++ = '\0';
546
547	return token;
548}
549
550int
551_ti_encode_buf_id_num(TBUF *tbuf, int ind, int num, size_t len)
552{
553	if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + len))
554		return 0;
555	_ti_encode_buf_16(tbuf, ind);
556	if (len == sizeof(uint32_t))
557		_ti_encode_buf_32(tbuf, num);
558	else
559		_ti_encode_buf_16(tbuf, num);
560	tbuf->entries++;
561	return 1;
562}
563
564int
565_ti_encode_buf_id_count_str(TBUF *tbuf, int ind, const void *buf, size_t len)
566{
567	if (!_ti_grow_tbuf(tbuf, 2 * sizeof(uint16_t) + len))
568		return 0;
569	_ti_encode_buf_16(tbuf, ind);
570	_ti_encode_buf_count_str(tbuf, buf, len);
571	tbuf->entries++;
572	return 1;
573}
574
575int
576_ti_encode_buf_id_flags(TBUF *tbuf, int ind, int flag)
577{
578	if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + 1))
579		return 0;
580	_ti_encode_buf_16(tbuf, ind);
581	tbuf->buf[tbuf->bufpos++] = flag;
582	tbuf->entries++;
583	return 1;
584}
585
586TIC *
587_ti_compile(char *cap, int flags)
588{
589	char *token, *p, *e, *name, *desc, *alias;
590	signed char flag;
591	long cnum;
592	short ind;
593	int num;
594	size_t len;
595	TBUF buf;
596	TIC *tic;
597
598	_DIAGASSERT(cap != NULL);
599
600	name = _ti_get_token(&cap, ',');
601	if (name == NULL) {
602		dowarn(flags, "no separator found: %s", cap);
603		return NULL;
604	}
605	desc = strrchr(name, '|');
606	if (desc != NULL)
607		*desc++ = '\0';
608	alias = strchr(name, '|');
609	if (alias != NULL)
610		*alias++ = '\0';
611
612	if (strlen(name) > UINT16_MAX - 1) {
613		dowarn(flags, "%s: name too long", name);
614		return NULL;
615	}
616	if (desc != NULL && strlen(desc) > UINT16_MAX - 1) {
617		dowarn(flags, "%s: description too long: %s", name, desc);
618		return NULL;
619	}
620	if (alias != NULL && strlen(alias) > UINT16_MAX - 1) {
621		dowarn(flags, "%s: alias too long: %s", name, alias);
622		return NULL;
623	}
624
625	tic = calloc(sizeof(*tic), 1);
626	if (tic == NULL)
627		return NULL;
628
629	tic->rtype = TERMINFO_RTYPE_O1; /* will promote if needed */
630	buf.buf = NULL;
631	buf.buflen = 0;
632
633	tic->name = _ti_getname(tic->rtype, name);
634	if (tic->name == NULL)
635		goto error;
636	if (alias != NULL && flags & TIC_ALIAS) {
637		tic->alias = _ti_getname(tic->rtype, alias);
638		if (tic->alias == NULL)
639			goto error;
640	}
641	if (desc != NULL && flags & TIC_DESCRIPTION) {
642		tic->desc = strdup(desc);
643		if (tic->desc == NULL)
644			goto error;
645	}
646
647	for (token = _ti_get_token(&cap, ',');
648	     token != NULL && *token != '\0';
649	     token = _ti_get_token(&cap, ','))
650	{
651		/* Skip commented caps */
652		if (!(flags & TIC_COMMENT) && token[0] == '.')
653			continue;
654
655		/* Obsolete entries */
656		if (token[0] == 'O' && token[1] == 'T') {
657			if (!(flags & TIC_EXTRA))
658				continue;
659			token += 2;
660		}
661
662		/* str cap */
663		p = strchr(token, '=');
664		if (p != NULL) {
665			*p++ = '\0';
666			/* Don't use the string if we already have it */
667			ind = (short)_ti_strindex(token);
668			if (ind != -1 &&
669			    _ti_find_cap(tic, &tic->strs, 's', ind) != NULL)
670				continue;
671
672			/* Encode the string to our scratch buffer */
673			buf.bufpos = 0;
674			if (encode_string(tic->name, token,
675				&buf, p, flags) == -1)
676				goto error;
677			if (buf.bufpos > UINT16_MAX - 1) {
678				dowarn(flags, "%s: %s: string is too long",
679				    tic->name, token);
680				continue;
681			}
682			if (!VALID_STRING(buf.buf)) {
683				dowarn(flags, "%s: %s: invalid string",
684				    tic->name, token);
685				continue;
686			}
687
688			if (ind == -1) {
689				if (!_ti_store_extra(tic, 1, token, 's', -1, -2,
690				    buf.buf, buf.bufpos, flags))
691					goto error;
692			} else {
693				if (!_ti_encode_buf_id_count_str(&tic->strs,
694				    ind, buf.buf, buf.bufpos))
695					goto error;
696			}
697			continue;
698		}
699
700		/* num cap */
701		p = strchr(token, '#');
702		if (p != NULL) {
703			*p++ = '\0';
704			/* Don't use the number if we already have it */
705			ind = (short)_ti_numindex(token);
706			if (ind != -1 &&
707			    _ti_find_cap(tic, &tic->nums, 'n', ind) != NULL)
708				continue;
709
710			cnum = strtol(p, &e, 0);
711			if (*e != '\0') {
712				dowarn(flags, "%s: %s: not a number",
713				    tic->name, token);
714				continue;
715			}
716			if (!VALID_NUMERIC(cnum) || cnum > INT32_MAX) {
717				dowarn(flags, "%s: %s: number %ld out of range",
718				    tic->name, token, cnum);
719				continue;
720			}
721			if (cnum > INT16_MAX) {
722				if (flags & TIC_COMPAT_V1)
723					cnum = INT16_MAX;
724				else if (tic->rtype == TERMINFO_RTYPE_O1)
725					if (_ti_promote(tic) == -1)
726						goto error;
727			}
728
729			num = (int)cnum;
730			if (ind == -1) {
731				if (!_ti_store_extra(tic, 1, token, 'n', -1,
732				    num, NULL, 0, flags))
733					goto error;
734			} else {
735				if (!_ti_encode_buf_id_num(&tic->nums,
736				    ind, num, _ti_numsize(tic)))
737					    goto error;
738			}
739			continue;
740		}
741
742		flag = 1;
743		len = strlen(token) - 1;
744		if (token[len] == '@') {
745			flag = CANCELLED_BOOLEAN;
746			token[len] = '\0';
747		}
748		ind = (short)_ti_flagindex(token);
749		if (ind == -1 && flag == CANCELLED_BOOLEAN) {
750			if ((ind = (short)_ti_numindex(token)) != -1) {
751				if (_ti_find_cap(tic, &tic->nums, 'n', ind)
752				    != NULL)
753					continue;
754				if (!_ti_encode_buf_id_num(&tic->nums, ind,
755				    CANCELLED_NUMERIC, _ti_numsize(tic)))
756					goto error;
757				continue;
758			} else if ((ind = (short)_ti_strindex(token)) != -1) {
759				if (_ti_find_cap(tic, &tic->strs, 's', ind)
760				    != NULL)
761					continue;
762				if (!_ti_encode_buf_id_num(
763				    &tic->strs, ind, 0, sizeof(uint16_t)))
764					goto error;
765				continue;
766			}
767		}
768		if (ind == -1) {
769			if (!_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL,
770			    0, flags))
771				goto error;
772		} else if (_ti_find_cap(tic, &tic->flags, 'f', ind) == NULL) {
773			if (!_ti_encode_buf_id_flags(&tic->flags, ind, flag))
774				goto error;
775		}
776	}
777
778	free(buf.buf);
779	return tic;
780
781error:
782	free(buf.buf);
783	_ti_freetic(tic);
784	return NULL;
785}
786
787void
788_ti_freetic(TIC *tic)
789{
790
791	if (tic != NULL) {
792		free(tic->name);
793		free(tic->alias);
794		free(tic->desc);
795		free(tic->extras.buf);
796		free(tic->flags.buf);
797		free(tic->nums.buf);
798		free(tic->strs.buf);
799		free(tic);
800	}
801}
802