Home | History | Annotate | Line # | Download | only in libterminfo
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 
     50 static void __attribute__((__format__(__printf__, 2, 3)))
     51 dowarn(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 
     63 char *
     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 
     85 char *
     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 
    119 char *
    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 
    155 size_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 
    207 ssize_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 
    308 static int
    309 encode_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 
    407 static char *
    408 get_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 
    437 TIC *
    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 
    633 error:
    634 	free(buf.buf);
    635 	_ti_freetic(tic);
    636 	return NULL;
    637 }
    638 
    639 void
    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