Home | History | Annotate | Line # | Download | only in libterminfo
compile.c revision 1.17
      1 /* $NetBSD: compile.c,v 1.17 2020/03/28 02:38:15 christos 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.17 2020/03/28 02:38:15 christos 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 
     54 static void __printflike(2, 3)
     55 dowarn(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 
     67 char *
     68 _ti_grow_tbuf(TBUF *tbuf, size_t len)
     69 {
     70 	char *buf;
     71 	size_t l;
     72 
     73 	_DIAGASSERT(tbuf != NULL);
     74 
     75 	l = tbuf->bufpos + len;
     76 	if (l > tbuf->buflen) {
     77 		if (tbuf->buflen == 0)
     78 			buf = malloc(l);
     79 		else
     80 			buf = realloc(tbuf->buf, l);
     81 		if (buf == NULL)
     82 			return NULL;
     83 		tbuf->buf = buf;
     84 		tbuf->buflen = l;
     85 	}
     86 	return tbuf->buf;
     87 }
     88 
     89 const char *
     90 _ti_find_cap(TIC *tic, TBUF *tbuf, char type, short ind)
     91 {
     92 	size_t n;
     93 	uint16_t num;
     94 	const char *cap;
     95 
     96 	_DIAGASSERT(tbuf != NULL);
     97 
     98 	cap = tbuf->buf;
     99 	for (n = tbuf->entries; n > 0; n--) {
    100 		num = _ti_decode_16(&cap);
    101 		if ((short)num == ind)
    102 			return cap;
    103 		switch (type) {
    104 		case 'f':
    105 			cap++;
    106 			break;
    107 		case 'n':
    108 			cap += _ti_numsize(tic);
    109 			break;
    110 		case 's':
    111 			num = _ti_decode_16(&cap);
    112 			cap += num;
    113 			break;
    114 		}
    115 	}
    116 
    117 	errno = ESRCH;
    118 	return NULL;
    119 }
    120 
    121 const char *
    122 _ti_find_extra(TIC *tic, TBUF *tbuf, const char *code)
    123 {
    124 	size_t n;
    125 	uint16_t num;
    126 	const char *cap;
    127 
    128 	_DIAGASSERT(tbuf != NULL);
    129 	_DIAGASSERT(code != NULL);
    130 
    131 	cap = tbuf->buf;
    132 	for (n = tbuf->entries; n > 0; n--) {
    133 		num = _ti_decode_16(&cap);
    134 		if (strcmp(cap, code) == 0)
    135 			return cap + num;
    136 		cap += num;
    137 		switch (*cap++) {
    138 		case 'f':
    139 			cap++;
    140 			break;
    141 		case 'n':
    142 			cap += _ti_numsize(tic);
    143 			break;
    144 		case 's':
    145 			num = _ti_decode_16(&cap);
    146 			cap += num;
    147 			break;
    148 		}
    149 	}
    150 
    151 	errno = ESRCH;
    152 	return NULL;
    153 }
    154 
    155 char *
    156 _ti_getname(int rtype, const char *orig)
    157 {
    158 	char *name;
    159 
    160 	if (rtype == TERMINFO_RTYPE) {
    161 		if (asprintf(&name, "%s@v3", orig) < 0)
    162 			name = NULL;
    163 	} else {
    164 		name = strdup(orig);
    165 	}
    166 	return name;
    167 }
    168 
    169 size_t
    170 _ti_store_extra(TIC *tic, int wrn, const char *id, char type, char flag,
    171     int num, const char *str, size_t strl, int flags)
    172 {
    173 	size_t l;
    174 
    175 	_DIAGASSERT(tic != NULL);
    176 
    177 	if (strcmp(id, "use") != 0) {
    178 		if (_ti_find_extra(tic, &tic->extras, id) != NULL)
    179 			return 0;
    180 		if (!(flags & TIC_EXTRA)) {
    181 			if (wrn != 0)
    182 				dowarn(flags, "%s: %s: unknown capability",
    183 				    tic->name, id);
    184 			return 0;
    185 		}
    186 	}
    187 
    188 	l = strlen(id) + 1;
    189 	if (l > UINT16_T_MAX) {
    190 		dowarn(flags, "%s: %s: cap name is too long", tic->name, id);
    191 		return 0;
    192 	}
    193 
    194 	if (!_ti_grow_tbuf(&tic->extras,
    195 		l + strl + sizeof(uint16_t) + _ti_numsize(tic) + 1))
    196 		return 0;
    197 	_ti_encode_buf_count_str(&tic->extras, id, l);
    198 	tic->extras.buf[tic->extras.bufpos++] = type;
    199 	switch (type) {
    200 	case 'f':
    201 		tic->extras.buf[tic->extras.bufpos++] = flag;
    202 		break;
    203 	case 'n':
    204 		_ti_encode_buf_num(&tic->extras, num, tic->rtype);
    205 		break;
    206 	case 's':
    207 		_ti_encode_buf_count_str(&tic->extras, str, strl);
    208 		break;
    209 	}
    210 	tic->extras.entries++;
    211 	return 1;
    212 }
    213 
    214 static void
    215 _ti_encode_buf(char **cap, const TBUF *buf)
    216 {
    217 	if (buf->entries == 0) {
    218 		_ti_encode_16(cap, 0);
    219 	} else {
    220 		_ti_encode_16(cap, buf->bufpos + sizeof(uint16_t));
    221 		_ti_encode_16(cap, buf->entries);
    222 		_ti_encode_str(cap, buf->buf, buf->bufpos);
    223 	}
    224 }
    225 
    226 ssize_t
    227 _ti_flatten(uint8_t **buf, const TIC *tic)
    228 {
    229 	size_t buflen, len, alen, dlen;
    230 	char *cap;
    231 
    232 	_DIAGASSERT(buf != NULL);
    233 	_DIAGASSERT(tic != NULL);
    234 
    235 	len = strlen(tic->name) + 1;
    236 	if (tic->alias == NULL)
    237 		alen = 0;
    238 	else
    239 		alen = strlen(tic->alias) + 1;
    240 	if (tic->desc == NULL)
    241 		dlen = 0;
    242 	else
    243 		dlen = strlen(tic->desc) + 1;
    244 
    245 	buflen = sizeof(char) +
    246 	    sizeof(uint16_t) + len +
    247 	    sizeof(uint16_t) + alen +
    248 	    sizeof(uint16_t) + dlen +
    249 	    (sizeof(uint16_t) * 2) + tic->flags.bufpos +
    250 	    (sizeof(uint16_t) * 2) + tic->nums.bufpos +
    251 	    (sizeof(uint16_t) * 2) + tic->strs.bufpos +
    252 	    (sizeof(uint16_t) * 2) + tic->extras.bufpos;
    253 
    254 	*buf = malloc(buflen);
    255 	if (*buf == NULL)
    256 		return -1;
    257 
    258 	cap = (char *)*buf;
    259 	*cap++ = tic->rtype;
    260 
    261 	_ti_encode_count_str(&cap, tic->name, len);
    262 	_ti_encode_count_str(&cap, tic->alias, alen);
    263 	_ti_encode_count_str(&cap, tic->desc, dlen);
    264 
    265 	_ti_encode_buf(&cap, &tic->flags);
    266 
    267 	_ti_encode_buf(&cap, &tic->nums);
    268 	_ti_encode_buf(&cap, &tic->strs);
    269 	_ti_encode_buf(&cap, &tic->extras);
    270 
    271 	return (uint8_t *)cap - *buf;
    272 }
    273 
    274 static int
    275 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
    276     int flags)
    277 {
    278 	int slash, i, num;
    279 	char ch, *p, *s, last;
    280 
    281 	if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
    282 		return -1;
    283 	p = s = tbuf->buf + tbuf->bufpos;
    284 	slash = 0;
    285 	last = '\0';
    286 	/* Convert escape codes */
    287 	while ((ch = *str++) != '\0') {
    288 		if (ch == '\n') {
    289 			/* Following a newline, strip leading whitespace from
    290 			 * capability strings. */
    291 			while (isspace((unsigned char)*str))
    292 				str++;
    293 			continue;
    294 		}
    295 		if (slash == 0 && ch == '\\') {
    296 			slash = 1;
    297 			continue;
    298 		}
    299 		if (slash == 0) {
    300 			if (last != '%' && ch == '^') {
    301 				ch = *str++;
    302 				if (((unsigned char)ch) >= 128)
    303 					dowarn(flags,
    304 					    "%s: %s: illegal ^ character",
    305 					    term, cap);
    306 				if (ch == '\0')
    307 					break;
    308 				if (ch == '?')
    309 					ch = '\177';
    310 				else if ((ch &= 037) == 0)
    311 					ch = (char)128;
    312 			} else if (!isprint((unsigned char)ch))
    313 				dowarn(flags,
    314 				    "%s: %s: unprintable character",
    315 				    term, cap);
    316 			*p++ = ch;
    317 			last = ch;
    318 			continue;
    319 		}
    320 		slash = 0;
    321 		if (ch >= '0' && ch <= '7') {
    322 			num = ch - '0';
    323 			for (i = 0; i < 2; i++) {
    324 				if (*str < '0' || *str > '7') {
    325 					if (isdigit((unsigned char)*str))
    326 						dowarn(flags,
    327 						    "%s: %s: non octal"
    328 						    " digit", term, cap);
    329 					else
    330 						break;
    331 				}
    332 				num = num * 8 + *str++ - '0';
    333 			}
    334 			if (num == 0)
    335 				num = 0200;
    336 			*p++ = (char)num;
    337 			continue;
    338 		}
    339 		switch (ch) {
    340 		case 'a':
    341 			*p++ = '\a';
    342 			break;
    343 		case 'b':
    344 			*p++ = '\b';
    345 			break;
    346 		case 'e': /* FALLTHROUGH */
    347 		case 'E':
    348 			*p++ = '\033';
    349 			break;
    350 		case 'f':
    351 			*p++ = '\014';
    352 			break;
    353 		case 'l': /* FALLTHROUGH */
    354 		case 'n':
    355 			*p++ = '\n';
    356 			break;
    357 		case 'r':
    358 			*p++ = '\r';
    359 			break;
    360 		case 's':
    361 			*p++ = ' ';
    362 			break;
    363 		case 't':
    364 			*p++ = '\t';
    365 			break;
    366 		default:
    367 			/* We should warn here */
    368 		case '^':
    369 		case ',':
    370 		case ':':
    371 		case '|':
    372 			*p++ = ch;
    373 			break;
    374 		}
    375 		last = ch;
    376 	}
    377 	*p++ = '\0';
    378 	tbuf->bufpos += (size_t)(p - s);
    379 	return 0;
    380 }
    381 
    382 char *
    383 _ti_get_token(char **cap, char sep)
    384 {
    385 	char esc, *token;
    386 
    387 	while (isspace((unsigned char)**cap))
    388 		(*cap)++;
    389 	if (**cap == '\0')
    390 		return NULL;
    391 
    392 	/* We can't use stresep(3) as ^ we need two escape chars */
    393 	esc = '\0';
    394 	for (token = *cap;
    395 	     **cap != '\0' && (esc != '\0' || **cap != sep);
    396 	     (*cap)++)
    397 	{
    398 		if (esc == '\0') {
    399 			if (**cap == '\\' || **cap == '^')
    400 				esc = **cap;
    401 		} else {
    402 			/* termcap /E/ is valid */
    403 			if (sep == ':' && esc == '\\' && **cap == 'E')
    404 				esc = 'x';
    405 			else
    406 				esc = '\0';
    407 		}
    408 	}
    409 
    410 	if (**cap != '\0')
    411 		*(*cap)++ = '\0';
    412 
    413 	return token;
    414 }
    415 
    416 static int
    417 _ti_find_rtype(const char *cap)
    418 {
    419 	const char *ptr;
    420 
    421 	for (ptr = cap; (ptr = strchr(ptr, '#')) != NULL;) {
    422 		if (strtol(++ptr, NULL, 0) > SHRT_MAX) {
    423 			return TERMINFO_RTYPE;
    424 		}
    425 	}
    426 	return TERMINFO_RTYPE_O1;
    427 }
    428 
    429 int
    430 _ti_encode_buf_id_num(TBUF *tbuf, int ind, int num, size_t len)
    431 {
    432 	if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + len))
    433 		return 0;
    434 	_ti_encode_buf_16(tbuf, ind);
    435 	if (len == sizeof(uint32_t))
    436 		_ti_encode_buf_32(tbuf, num);
    437 	else
    438 		_ti_encode_buf_16(tbuf, num);
    439 	tbuf->entries++;
    440 	return 1;
    441 }
    442 
    443 int
    444 _ti_encode_buf_id_count_str(TBUF *tbuf, int ind, const void *buf, size_t len)
    445 {
    446 	if (!_ti_grow_tbuf(tbuf, 2 * sizeof(uint16_t) + len))
    447 		return 0;
    448 	_ti_encode_buf_16(tbuf, ind);
    449 	_ti_encode_buf_count_str(tbuf, buf, len);
    450 	tbuf->entries++;
    451 	return 1;
    452 }
    453 
    454 int
    455 _ti_encode_buf_id_flags(TBUF *tbuf, int ind, int flag)
    456 {
    457 	if (!_ti_grow_tbuf(tbuf, sizeof(uint16_t) + 1))
    458 		return 0;
    459 	_ti_encode_buf_16(tbuf, ind);
    460 	tbuf->buf[tbuf->bufpos++] = flag;
    461 	tbuf->entries++;
    462 	return 1;
    463 }
    464 
    465 TIC *
    466 _ti_compile(char *cap, int flags)
    467 {
    468 	char *token, *p, *e, *name, *desc, *alias;
    469 	signed char flag;
    470 	long cnum;
    471 	short ind;
    472 	int num;
    473 	size_t len;
    474 	TBUF buf;
    475 	TIC *tic;
    476 
    477 	_DIAGASSERT(cap != NULL);
    478 
    479 	name = _ti_get_token(&cap, ',');
    480 	if (name == NULL) {
    481 		dowarn(flags, "no separator found: %s", cap);
    482 		return NULL;
    483 	}
    484 	desc = strrchr(name, '|');
    485 	if (desc != NULL)
    486 		*desc++ = '\0';
    487 	alias = strchr(name, '|');
    488 	if (alias != NULL)
    489 		*alias++ = '\0';
    490 
    491 	if (strlen(name) > UINT16_MAX - 1) {
    492 		dowarn(flags, "%s: name too long", name);
    493 		return NULL;
    494 	}
    495 	if (desc != NULL && strlen(desc) > UINT16_MAX - 1) {
    496 		dowarn(flags, "%s: description too long: %s", name, desc);
    497 		return NULL;
    498 	}
    499 	if (alias != NULL && strlen(alias) > UINT16_MAX - 1) {
    500 		dowarn(flags, "%s: alias too long: %s", name, alias);
    501 		return NULL;
    502 	}
    503 
    504 	tic = calloc(sizeof(*tic), 1);
    505 	if (tic == NULL)
    506 		return NULL;
    507 
    508 	tic->rtype = (flags & TIC_COMPAT_V1) ? TERMINFO_RTYPE_O1 :
    509 	    _ti_find_rtype(cap);
    510 	buf.buf = NULL;
    511 	buf.buflen = 0;
    512 
    513 	tic->name = _ti_getname(tic->rtype, name);
    514 	if (tic->name == NULL)
    515 		goto error;
    516 	if (alias != NULL && flags & TIC_ALIAS) {
    517 		tic->alias = _ti_getname(tic->rtype, alias);
    518 		if (tic->alias == NULL)
    519 			goto error;
    520 	}
    521 	if (desc != NULL && flags & TIC_DESCRIPTION) {
    522 		tic->desc = strdup(desc);
    523 		if (tic->desc == NULL)
    524 			goto error;
    525 	}
    526 
    527 	for (token = _ti_get_token(&cap, ',');
    528 	     token != NULL && *token != '\0';
    529 	     token = _ti_get_token(&cap, ','))
    530 	{
    531 		/* Skip commented caps */
    532 		if (!(flags & TIC_COMMENT) && token[0] == '.')
    533 			continue;
    534 
    535 		/* Obsolete entries */
    536 		if (token[0] == 'O' && token[1] == 'T') {
    537 			if (!(flags & TIC_EXTRA))
    538 				continue;
    539 			token += 2;
    540 		}
    541 
    542 		/* str cap */
    543 		p = strchr(token, '=');
    544 		if (p != NULL) {
    545 			*p++ = '\0';
    546 			/* Don't use the string if we already have it */
    547 			ind = (short)_ti_strindex(token);
    548 			if (ind != -1 &&
    549 			    _ti_find_cap(tic, &tic->strs, 's', ind) != NULL)
    550 				continue;
    551 
    552 			/* Encode the string to our scratch buffer */
    553 			buf.bufpos = 0;
    554 			if (encode_string(tic->name, token,
    555 				&buf, p, flags) == -1)
    556 				goto error;
    557 			if (buf.bufpos > UINT16_MAX - 1) {
    558 				dowarn(flags, "%s: %s: string is too long",
    559 				    tic->name, token);
    560 				continue;
    561 			}
    562 			if (!VALID_STRING(buf.buf)) {
    563 				dowarn(flags, "%s: %s: invalid string",
    564 				    tic->name, token);
    565 				continue;
    566 			}
    567 
    568 			if (ind == -1) {
    569 				if (!_ti_store_extra(tic, 1, token, 's', -1, -2,
    570 				    buf.buf, buf.bufpos, flags))
    571 					goto error;
    572 			} else {
    573 				if (!_ti_encode_buf_id_count_str(&tic->strs,
    574 				    ind, buf.buf, buf.bufpos))
    575 					goto error;
    576 			}
    577 			continue;
    578 		}
    579 
    580 		/* num cap */
    581 		p = strchr(token, '#');
    582 		if (p != NULL) {
    583 			*p++ = '\0';
    584 			/* Don't use the number if we already have it */
    585 			ind = (short)_ti_numindex(token);
    586 			if (ind != -1 &&
    587 			    _ti_find_cap(tic, &tic->nums, 'n', ind) != NULL)
    588 				continue;
    589 
    590 			cnum = strtol(p, &e, 0);
    591 			if (*e != '\0') {
    592 				dowarn(flags, "%s: %s: not a number",
    593 				    tic->name, token);
    594 				continue;
    595 			}
    596 			if (!VALID_NUMERIC(cnum) || cnum > INT32_MAX) {
    597 				dowarn(flags, "%s: %s: number %ld out of range",
    598 				    tic->name, token, cnum);
    599 				continue;
    600 			}
    601 
    602 			num = (int)cnum;
    603 			if (ind == -1) {
    604 				if (!_ti_store_extra(tic, 1, token, 'n', -1,
    605 				    num, NULL, 0, flags))
    606 					goto error;
    607 			} else {
    608 				if (!_ti_encode_buf_id_num(&tic->nums,
    609 				    ind, num, _ti_numsize(tic)))
    610 					    goto error;
    611 			}
    612 			continue;
    613 		}
    614 
    615 		flag = 1;
    616 		len = strlen(token) - 1;
    617 		if (token[len] == '@') {
    618 			flag = CANCELLED_BOOLEAN;
    619 			token[len] = '\0';
    620 		}
    621 		ind = (short)_ti_flagindex(token);
    622 		if (ind == -1 && flag == CANCELLED_BOOLEAN) {
    623 			if ((ind = (short)_ti_numindex(token)) != -1) {
    624 				if (_ti_find_cap(tic, &tic->nums, 'n', ind)
    625 				    != NULL)
    626 					continue;
    627 				if (!_ti_encode_buf_id_num(&tic->nums, ind,
    628 				    CANCELLED_NUMERIC, _ti_numsize(tic)))
    629 					goto error;
    630 				continue;
    631 			} else if ((ind = (short)_ti_strindex(token)) != -1) {
    632 				if (_ti_find_cap(tic, &tic->strs, 's', ind)
    633 				    != NULL)
    634 					continue;
    635 				if (!_ti_encode_buf_id_num(
    636 				    &tic->strs, ind, 0, sizeof(uint16_t)))
    637 					goto error;
    638 				continue;
    639 			}
    640 		}
    641 		if (ind == -1) {
    642 			if (!_ti_store_extra(tic, 1, token, 'f', flag, 0, NULL,
    643 			    0, flags))
    644 				goto error;
    645 		} else if (_ti_find_cap(tic, &tic->flags, 'f', ind) == NULL) {
    646 			if (!_ti_encode_buf_id_flags(&tic->flags, ind, flags))
    647 				goto error;
    648 		}
    649 	}
    650 
    651 	free(buf.buf);
    652 	return tic;
    653 
    654 error:
    655 	free(buf.buf);
    656 	_ti_freetic(tic);
    657 	return NULL;
    658 }
    659 
    660 void
    661 _ti_freetic(TIC *tic)
    662 {
    663 
    664 	if (tic != NULL) {
    665 		free(tic->name);
    666 		free(tic->alias);
    667 		free(tic->desc);
    668 		free(tic->extras.buf);
    669 		free(tic->flags.buf);
    670 		free(tic->nums.buf);
    671 		free(tic->strs.buf);
    672 		free(tic);
    673 	}
    674 }
    675