Home | History | Annotate | Line # | Download | only in tic
tic.c revision 1.9
      1 /* $NetBSD: tic.c,v 1.9 2010/02/20 06:20:46 pgoyette 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: tic.c,v 1.9 2010/02/20 06:20:46 pgoyette Exp $");
     36 
     37 #include <sys/types.h>
     38 
     39 #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
     40 #include <sys/endian.h>
     41 #endif
     42 
     43 #include <ctype.h>
     44 #include <err.h>
     45 #include <errno.h>
     46 #include <getopt.h>
     47 #include <limits.h>
     48 #include <fcntl.h>
     49 #include <ndbm.h>
     50 #include <stdarg.h>
     51 #include <stdlib.h>
     52 #include <stdio.h>
     53 #include <string.h>
     54 #include <term_private.h>
     55 #include <term.h>
     56 
     57 #define UINT16_T_MAX 0xffff
     58 
     59 typedef struct tbuf {
     60 	char *buf;
     61 	size_t buflen;
     62 	size_t bufpos;
     63 	size_t entries;
     64 } TBUF;
     65 
     66 typedef struct tic {
     67 	char *name;
     68 	char *alias;
     69 	char *desc;
     70 	TBUF flags;
     71 	TBUF nums;
     72 	TBUF strs;
     73 	TBUF extras;
     74 } TIC;
     75 
     76 /* We store the full list of terminals we have instead of iterating
     77    through the database as the sequential iterator doesn't work
     78    the the data size stored changes N amount which ours will. */
     79 typedef struct term {
     80 	struct term *next;
     81 	char *name;
     82 	char type;
     83 	TIC tic;
     84 } TERM;
     85 static TERM *terms;
     86 
     87 static int error_exit;
     88 static int Sflag, aflag, xflag;
     89 static char *dbname;
     90 
     91 static TBUF scratch;
     92 
     93 static void
     94 do_unlink(void)
     95 {
     96 
     97 	if (dbname != NULL)
     98 		unlink(dbname);
     99 }
    100 
    101 static void __attribute__((__format__(__printf__, 1, 2)))
    102 dowarn(const char *fmt, ...)
    103 {
    104 	va_list va;
    105 
    106 	error_exit = 1;
    107 	va_start(va, fmt);
    108 	vwarnx(fmt, va);
    109 	va_end(va);
    110 }
    111 
    112 static char *
    113 grow_tbuf(TBUF *tbuf, size_t len)
    114 {
    115 	char *buf;
    116 	size_t l;
    117 
    118 	l = tbuf->bufpos + len;
    119 	if (l > tbuf->buflen) {
    120 		if (tbuf->bufpos == 0) {
    121 			buf = malloc(l);
    122 			if (buf == NULL)
    123 				err(1, "malloc (%zu bytes)", l);
    124 		} else {
    125 			buf = realloc(tbuf->buf, l);
    126 			if (buf == NULL)
    127 				err(1, "realloc (%zu bytes)", l);
    128 		}
    129 		tbuf->buf = buf;
    130 		tbuf->buflen = l;
    131 	}
    132 	return tbuf->buf;
    133 }
    134 
    135 static char *
    136 find_cap(TBUF *tbuf, char type, short ind)
    137 {
    138 	size_t n;
    139 	short num;
    140 	char *cap;
    141 
    142 	cap = tbuf->buf;
    143 	for (n = tbuf->entries; n > 0; n--) {
    144 		num = le16dec(cap);
    145 		cap += sizeof(uint16_t);
    146 		if (num == ind)
    147 			return cap;
    148 		switch (type) {
    149 		case 'f':
    150 			cap++;
    151 			break;
    152 		case 'n':
    153 			cap += sizeof(uint16_t);
    154 			break;
    155 		case 's':
    156 			num = le16dec(cap);
    157 			cap += sizeof(uint16_t);
    158 			cap += num;
    159 			break;
    160 		}
    161 	}
    162 	return NULL;
    163 }
    164 
    165 static char *
    166 find_extra(TBUF *tbuf, const char *code)
    167 {
    168 	size_t n;
    169 	short num;
    170 	char *cap;
    171 
    172 	cap = tbuf->buf;
    173 	for (n = tbuf->entries; n > 0; n--) {
    174 		num = le16dec(cap);
    175 		cap += sizeof(uint16_t);
    176 		if (strcmp(cap, code) == 0)
    177 			return cap + num;
    178 		cap += num;
    179 		switch (*cap++) {
    180 		case 'f':
    181 			cap++;
    182 			break;
    183 		case 'n':
    184 			cap += sizeof(uint16_t);
    185 			break;
    186 		case 's':
    187 			num = le16dec(cap);
    188 			cap += sizeof(uint16_t);
    189 			cap += num;
    190 			break;
    191 		}
    192 	}
    193 	return NULL;
    194 }
    195 
    196 static size_t
    197 store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num,
    198     char *str, size_t strl)
    199 {
    200 	size_t l;
    201 
    202 	if (strcmp(id, "use") != 0) {
    203 		if (find_extra(&tic->extras, id) != NULL)
    204 			return 0;
    205 		if (!xflag) {
    206 			if (wrn != 0)
    207 				dowarn("%s: %s: unknown capability",
    208 				    tic->name, id);
    209 			return 0;
    210 		}
    211 	}
    212 
    213 	l = strlen(id) + 1;
    214 	if (l > UINT16_T_MAX) {
    215 		dowarn("%s: %s: cap name is too long", tic->name, id);
    216 		return 0;
    217 	}
    218 
    219 	grow_tbuf(&tic->extras, l + strl + (sizeof(uint16_t) * 2) + 1);
    220 	le16enc(tic->extras.buf + tic->extras.bufpos, l);
    221 	tic->extras.bufpos += sizeof(uint16_t);
    222 	memcpy(tic->extras.buf + tic->extras.bufpos, id, l);
    223 	tic->extras.bufpos += l;
    224 	tic->extras.buf[tic->extras.bufpos++] = type;
    225 	switch (type) {
    226 	case 'f':
    227 		tic->extras.buf[tic->extras.bufpos++] = flag;
    228 		break;
    229 	case 'n':
    230 		le16enc(tic->extras.buf + tic->extras.bufpos, num);
    231 		tic->extras.bufpos += sizeof(uint16_t);
    232 		break;
    233 	case 's':
    234 		le16enc(tic->extras.buf + tic->extras.bufpos, strl);
    235 		tic->extras.bufpos += sizeof(uint16_t);
    236 		memcpy(tic->extras.buf + tic->extras.bufpos, str, strl);
    237 		tic->extras.bufpos += strl;
    238 		break;
    239 	}
    240 	tic->extras.entries++;
    241 	return 1;
    242 }
    243 
    244 static TBUF *
    245 flatten_term(TERM *term)
    246 {
    247 	size_t buflen, len, alen, dlen;
    248 	char *cap;
    249 	TIC *tic;
    250 
    251 	scratch.bufpos = 0;
    252 	tic = &term->tic;
    253 	len = strlen(tic->name) + 1;
    254 	if (tic->alias == NULL || Sflag)
    255 		alen = 0;
    256 	else
    257 		alen = strlen(tic->alias) + 1;
    258 	if (tic->desc == NULL || Sflag)
    259 		dlen = 0;
    260 	else
    261 		dlen = strlen(tic->desc) + 1;
    262 	buflen = sizeof(char) +
    263 	    sizeof(uint16_t) + len +
    264 	    sizeof(uint16_t) + alen +
    265 	    sizeof(uint16_t) + dlen +
    266 	    (sizeof(uint16_t) * 2) + tic->flags.bufpos +
    267 	    (sizeof(uint16_t) * 2) + tic->nums.bufpos +
    268 	    (sizeof(uint16_t) * 2) + tic->strs.bufpos +
    269 	    (sizeof(uint16_t) * 2) + tic->extras.bufpos;
    270 	grow_tbuf(&scratch, buflen);
    271 	cap = scratch.buf;
    272 	if (term->type == 'a')
    273 		*cap++ = 0;
    274 	else
    275 		*cap++ = 2; /* version */
    276 	le16enc(cap, len);
    277 	cap += sizeof(uint16_t);
    278 	memcpy(cap, tic->name, len);
    279 	cap += len;
    280 	if (term->type != 'a') {
    281 		le16enc(cap, alen);
    282 		cap += sizeof(uint16_t);
    283 		if (tic->alias != NULL && !Sflag) {
    284 			memcpy(cap, tic->alias, alen);
    285 			cap += alen;
    286 		}
    287 		le16enc(cap, dlen);
    288 		cap += sizeof(uint16_t);
    289 		if (tic->desc != NULL && !Sflag) {
    290 			memcpy(cap, tic->desc, dlen);
    291 			cap += dlen;
    292 		}
    293 
    294 		if (tic->flags.entries == 0) {
    295 			le16enc(cap, 0);
    296 			cap += sizeof(uint16_t);
    297 		} else {
    298 			le16enc(cap, (tic->flags.bufpos + sizeof(uint16_t)));
    299 			cap += sizeof(uint16_t);
    300 			le16enc(cap, tic->flags.entries);
    301 			cap += sizeof(uint16_t);
    302 			memcpy(cap, tic->flags.buf, tic->flags.bufpos);
    303 			cap += tic->flags.bufpos;
    304 		}
    305 
    306 		if (tic->nums.entries == 0) {
    307 			le16enc(cap, 0);
    308 			cap += sizeof(uint16_t);
    309 		} else {
    310 			le16enc(cap, (tic->nums.bufpos + sizeof(uint16_t)));
    311 			cap += sizeof(uint16_t);
    312 			le16enc(cap, tic->nums.entries);
    313 			cap += sizeof(uint16_t);
    314 			memcpy(cap, tic->nums.buf, tic->nums.bufpos);
    315 			cap += tic->nums.bufpos;
    316 		}
    317 
    318 		if (tic->strs.entries == 0) {
    319 			le16enc(cap, 0);
    320 			cap += sizeof(uint16_t);
    321 		} else {
    322 			le16enc(cap, (tic->strs.bufpos + sizeof(uint16_t)));
    323 			cap += sizeof(uint16_t);
    324 			le16enc(cap, tic->strs.entries);
    325 			cap += sizeof(uint16_t);
    326 			memcpy(cap, tic->strs.buf, tic->strs.bufpos);
    327 			cap += tic->strs.bufpos;
    328 		}
    329 
    330 		if (tic->extras.entries == 0) {
    331 			le16enc(cap, 0);
    332 			cap += sizeof(uint16_t);
    333 		} else {
    334 			le16enc(cap, (tic->extras.bufpos + sizeof(uint16_t)));
    335 			cap += sizeof(uint16_t);
    336 			le16enc(cap, tic->extras.entries);
    337 			cap += sizeof(uint16_t);
    338 			memcpy(cap, tic->extras.buf, tic->extras.bufpos);
    339 			cap += tic->extras.bufpos;
    340 		}
    341 	}
    342 	scratch.bufpos = cap - scratch.buf;
    343 
    344 	return &scratch;
    345 }
    346 
    347 static int
    348 save_term(DBM *db, TERM *term)
    349 {
    350 	TBUF *buf;
    351 	datum key, value;
    352 
    353 	buf = flatten_term(term);
    354 	if (buf == NULL)
    355 		return -1;
    356 
    357 	key.dptr = term->name;
    358 	key.dsize = strlen(term->name);
    359 	value.dptr = scratch.buf;
    360 	value.dsize = scratch.bufpos;
    361 	if (dbm_store(db, key, value, DBM_REPLACE) == -1)
    362 		err(1, "dbm_store");
    363 	return 0;
    364 }
    365 
    366 static TERM *
    367 find_term(const char *name)
    368 {
    369 	TERM *term;
    370 
    371 	for (term = terms; term != NULL; term = term->next)
    372 		if (strcmp(term->name, name) == 0)
    373 			return term;
    374 	return NULL;
    375 }
    376 
    377 static TERM *
    378 store_term(const char *name, char type)
    379 {
    380 	TERM *term;
    381 
    382 	term = calloc(1, sizeof(*term));
    383 	if (term == NULL)
    384 		errx(1, "malloc");
    385 	term->name = strdup(name);
    386 	term->type = type;
    387 	if (term->name == NULL)
    388 		errx(1, "malloc");
    389 	term->next = terms;
    390 	terms = term;
    391 	return term;
    392 }
    393 
    394 static void
    395 encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str)
    396 {
    397 	int slash, i, num;
    398 	char ch, *p, *s, last;
    399 
    400 	grow_tbuf(tbuf, strlen(str) + 1);
    401 	p = s = tbuf->buf + tbuf->bufpos;
    402 	slash = 0;
    403 	last = '\0';
    404 	/* Convert escape codes */
    405 	while ((ch = *str++) != '\0') {
    406 		if (slash == 0 && ch == '\\') {
    407 			slash = 1;
    408 			continue;
    409 		}
    410 		if (slash == 0) {
    411 			if (last != '%' && ch == '^') {
    412 				ch = *str++;
    413 				if (((unsigned char)ch) >= 128)
    414 					dowarn("%s: %s: illegal ^ character",
    415 					    term, cap);
    416 				if (ch == '\0')
    417 					break;
    418 				if (ch == '?')
    419 					ch = '\177';
    420 				else if ((ch &= 037) == 0)
    421 					ch = 128;
    422 			}
    423 			*p++ = ch;
    424 			last = ch;
    425 			continue;
    426 		}
    427 		slash = 0;
    428 		if (ch >= '0' && ch <= '7') {
    429 			num = ch - '0';
    430 			for (i = 0; i < 2; i++) {
    431 				if (*str < '0' || *str > '7') {
    432 					if (isdigit((unsigned char)*str))
    433 						dowarn("%s: %s: non octal"
    434 						    " digit", term, cap);
    435 					else
    436 						break;
    437 				}
    438 				num = num * 8 + *str++ - '0';
    439 			}
    440 			if (num == 0)
    441 				num = 0200;
    442 			*p++ = (char)num;
    443 			continue;
    444 		}
    445 		switch (ch) {
    446 		case 'a':
    447 			*p++ = '\a';
    448 			break;
    449 		case 'b':
    450 			*p++ = '\b';
    451 			break;
    452 		case 'e': /* FALLTHROUGH */
    453 		case 'E':
    454 			*p++ = '\033';
    455 			break;
    456 		case 'f':
    457 			*p++ = '\014';
    458 			break;
    459 		case 'l': /* FALLTHROUGH */
    460 		case 'n':
    461 			*p++ = '\n';
    462 			break;
    463 		case 'r':
    464 			*p++ = '\r';
    465 			break;
    466 		case 's':
    467 			*p++ = ' ';
    468 			break;
    469 		case 't':
    470 			*p++ = '\t';
    471 			break;
    472 		default:
    473 
    474 			/* We should warn here */
    475 		case '^':
    476 		case ',':
    477 		case ':':
    478 		case '|':
    479 			*p++ = ch;
    480 			break;
    481 		}
    482 		last = ch;
    483 	}
    484 	*p++ = '\0';
    485 	tbuf->bufpos += p - s;
    486 }
    487 
    488 static int
    489 process_entry(TBUF *buf)
    490 {
    491 	char *cap, *capstart, *p, *e, *name, *desc, *alias;
    492 	signed char flag;
    493 	long num;
    494 	int slash;
    495 	ssize_t ind;
    496 	size_t len;
    497 	TERM *term;
    498 	TIC *tic;
    499 
    500 	if (buf->bufpos == 0)
    501 		return 0;
    502 	/* Terminate the string */
    503 	buf->buf[buf->bufpos - 1] = '\0';
    504 	/* First rewind the buffer for new entries */
    505 	buf->bufpos = 0;
    506 
    507 	if (isspace((unsigned char)*buf->buf))
    508 		return 0;
    509 
    510 	cap = strchr(buf->buf, '\n');
    511 	if (cap == NULL)
    512 		return 0;
    513 	e = cap - 1;
    514 	if (*e == ',')
    515 		*e = '\0';
    516 	*cap++ = '\0';
    517 
    518 	name = buf->buf;
    519 	desc = strrchr(buf->buf, '|');
    520 	if (desc != NULL)
    521 		*desc++ = '\0';
    522 	alias = strchr(buf->buf, '|');
    523 	if (alias != NULL)
    524 		*alias++ = '\0';
    525 
    526 	if (*e != '\0')
    527 		dowarn("%s: description missing separator", buf->buf);
    528 
    529 	/* If we already have this term, abort */
    530 	if (find_term(name) != NULL) {
    531 		dowarn("%s: duplicate entry", name);
    532 		return 0;
    533 	}
    534 	term = store_term(name, 't');
    535 	tic = &term->tic;
    536 	tic->name = strdup(name);
    537 	if (tic->name == NULL)
    538 		err(1, "malloc");
    539 	if (alias != NULL) {
    540 		tic->alias = strdup(alias);
    541 		if (tic->alias == NULL)
    542 			err(1, "malloc");
    543 	}
    544 	if (desc != NULL) {
    545 		tic->desc = strdup(desc);
    546 		if (tic->desc == NULL)
    547 			err(1, "malloc");
    548 	}
    549 
    550 	do {
    551 		while (isspace((unsigned char)*cap))
    552 			cap++;
    553 		if (*cap == '\0')
    554 			break;
    555 		slash = 0;
    556 		for (capstart = cap;
    557 		     *cap != '\0' && (slash == 1 || *cap != ',');
    558 		     cap++)
    559 		{
    560 			if (slash == 0) {
    561 				if (*cap == '\\')
    562 					slash = 1;
    563 			} else
    564 				slash = 0;
    565 			continue;
    566 		}
    567 		*cap++ = '\0';
    568 
    569 		/* Skip commented caps */
    570 		if (!aflag && capstart[0] == '.')
    571 			continue;
    572 
    573 		/* Obsolete entries */
    574 		if (capstart[0] == 'O' && capstart[1] == 'T') {
    575 			if (!xflag)
    576 				continue;
    577 			capstart += 2;
    578 		}
    579 
    580 		/* str cap */
    581 		p = strchr(capstart, '=');
    582 		if (p != NULL) {
    583 			*p++ = '\0';
    584 			/* Don't use the string if we already have it */
    585 			ind = _ti_strindex(capstart);
    586 			if (ind != -1 &&
    587 			    find_cap(&tic->strs, 's', ind) != NULL)
    588 				continue;
    589 
    590 			/* Encode the string to our scratch buffer */
    591 			scratch.bufpos = 0;
    592 			encode_string(tic->name, capstart, &scratch, p);
    593 			if (scratch.bufpos > UINT16_T_MAX) {
    594 				dowarn("%s: %s: string is too long",
    595 				    tic->name, capstart);
    596 				continue;
    597 			}
    598 			if (!VALID_STRING(scratch.buf)) {
    599 				dowarn("%s: %s: invalid string",
    600 				    tic->name, capstart);
    601 				continue;
    602 			}
    603 
    604 			if (ind == -1)
    605 				store_extra(tic, 1, capstart, 's', -1, -2,
    606 				    scratch.buf, scratch.bufpos);
    607 			else {
    608 				grow_tbuf(&tic->strs, (sizeof(uint16_t) * 2) +
    609 				    scratch.bufpos);
    610 				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
    611 				tic->strs.bufpos += sizeof(uint16_t);
    612 				le16enc(tic->strs.buf + tic->strs.bufpos,
    613 				    scratch.bufpos);
    614 				tic->strs.bufpos += sizeof(uint16_t);
    615 				memcpy(tic->strs.buf + tic->strs.bufpos,
    616 				    scratch.buf, scratch.bufpos);
    617 				tic->strs.bufpos += scratch.bufpos;
    618 				tic->strs.entries++;
    619 			}
    620 			continue;
    621 		}
    622 
    623 		/* num cap */
    624 		p = strchr(capstart, '#');
    625 		if (p != NULL) {
    626 			*p++ = '\0';
    627 			/* Don't use the number if we already have it */
    628 			ind = _ti_numindex(capstart);
    629 			if (ind != -1 &&
    630 			    find_cap(&tic->nums, 'n', ind) != NULL)
    631 				continue;
    632 
    633 			num = strtol(p, &e, 0);
    634 			if (*e != '\0') {
    635 				dowarn("%s: %s: not a number",
    636 				    tic->name, capstart);
    637 				continue;
    638 			}
    639 			if (!VALID_NUMERIC(num)) {
    640 				dowarn("%s: %s: number out of range",
    641 				    tic->name, capstart);
    642 				continue;
    643 			}
    644 			if (ind == -1)
    645 				store_extra(tic, 1, capstart, 'n', -1,
    646 				    num, NULL, 0);
    647 			else {
    648 				grow_tbuf(&tic->nums, sizeof(uint16_t) * 2);
    649 				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
    650 				tic->nums.bufpos += sizeof(uint16_t);
    651 				le16enc(tic->nums.buf + tic->nums.bufpos, num);
    652 				tic->nums.bufpos += sizeof(uint16_t);
    653 				tic->nums.entries++;
    654 			}
    655 			continue;
    656 		}
    657 
    658 		flag = 1;
    659 		len = strlen(capstart) - 1;
    660 		if (capstart[len] == '@') {
    661 			flag = CANCELLED_BOOLEAN;
    662 			capstart[len] = '\0';
    663 		}
    664 		ind = _ti_flagindex(capstart);
    665 		if (ind == -1 && flag == CANCELLED_BOOLEAN) {
    666 			if ((ind = _ti_numindex(capstart)) != -1) {
    667 				if (find_cap(&tic->nums, 'n', ind) != NULL)
    668 					continue;
    669 				grow_tbuf(&tic->nums, sizeof(uint16_t) * 2);
    670 				le16enc(tic->nums.buf + tic->nums.bufpos, ind);
    671 				tic->nums.bufpos += sizeof(uint16_t);
    672 				le16enc(tic->nums.buf + tic->nums.bufpos,
    673 					CANCELLED_NUMERIC);
    674 				tic->nums.bufpos += sizeof(uint16_t);
    675 				tic->nums.entries++;
    676 				continue;
    677 			} else if ((ind = _ti_strindex(capstart)) != -1) {
    678 				if (find_cap(&tic->strs, 's', ind) != NULL)
    679 					continue;
    680 				grow_tbuf(&tic->strs,
    681 				    (sizeof(uint16_t) * 2) + 1);
    682 				le16enc(tic->strs.buf + tic->strs.bufpos, ind);
    683 				tic->strs.bufpos += sizeof(uint16_t);
    684 				le16enc(tic->strs.buf + tic->strs.bufpos, 0);
    685 				tic->strs.bufpos += sizeof(uint16_t);
    686 				tic->strs.entries++;
    687 				continue;
    688 			}
    689 		}
    690 		if (ind == -1)
    691 			store_extra(tic, 1, capstart, 'f', flag, 0, NULL, 0);
    692 		else if (find_cap(&tic->flags, 'f', ind) == NULL) {
    693 			grow_tbuf(&tic->flags, sizeof(uint16_t) + 1);
    694 			le16enc(tic->flags.buf + tic->flags.bufpos, ind);
    695 			tic->flags.bufpos += sizeof(uint16_t);
    696 			tic->flags.buf[tic->flags.bufpos++] = flag;
    697 			tic->flags.entries++;
    698 		}
    699 	} while (*cap == ',' || isspace((unsigned char)*cap));
    700 
    701 	/* Create aliased terms */
    702 	if (alias != NULL) {
    703 		while (alias != NULL && *alias != '\0') {
    704 			desc = strchr(alias, '|');
    705 			if (desc != NULL)
    706 				*desc++ = '\0';
    707 			if (find_term(alias) != NULL) {
    708 				dowarn("%s: has alias for already assigned"
    709 				    " term %s", tic->name, alias);
    710 			} else {
    711 				term = store_term(alias, 'a');
    712 				term->tic.name = strdup(tic->name);
    713 				if (term->tic.name == NULL)
    714 					err(1, "malloc");
    715 			}
    716 			alias = desc;
    717 		}
    718 	}
    719 
    720 	return 0;
    721 }
    722 
    723 static void
    724 merge(TIC *rtic, TIC *utic)
    725 {
    726 	char *cap, flag, *code, type, *str;
    727 	short ind, num;
    728 	size_t n;
    729 
    730 	cap = utic->flags.buf;
    731 	for (n = utic->flags.entries; n > 0; n--) {
    732 		ind = le16dec(cap);
    733 		cap += sizeof(uint16_t);
    734 		flag = *cap++;
    735 		if (VALID_BOOLEAN(flag) &&
    736 		    find_cap(&rtic->flags, 'f', ind) == NULL)
    737 		{
    738 			grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1);
    739 			le16enc(rtic->flags.buf + rtic->flags.bufpos, ind);
    740 			rtic->flags.bufpos += sizeof(uint16_t);
    741 			rtic->flags.buf[rtic->flags.bufpos++] = flag;
    742 			rtic->flags.entries++;
    743 		}
    744 	}
    745 
    746 	cap = utic->nums.buf;
    747 	for (n = utic->nums.entries; n > 0; n--) {
    748 		ind = le16dec(cap);
    749 		cap += sizeof(uint16_t);
    750 		num = le16dec(cap);
    751 		cap += sizeof(uint16_t);
    752 		if (VALID_NUMERIC(num) &&
    753 		    find_cap(&rtic->nums, 'n', ind) == NULL)
    754 		{
    755 			grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2);
    756 			le16enc(rtic->nums.buf + rtic->nums.bufpos, ind);
    757 			rtic->nums.bufpos += sizeof(uint16_t);
    758 			le16enc(rtic->nums.buf + rtic->nums.bufpos, num);
    759 			rtic->nums.bufpos += sizeof(uint16_t);
    760 			rtic->nums.entries++;
    761 		}
    762 	}
    763 
    764 	cap = utic->strs.buf;
    765 	for (n = utic->strs.entries; n > 0; n--) {
    766 		ind = le16dec(cap);
    767 		cap += sizeof(uint16_t);
    768 		num = le16dec(cap);
    769 		cap += sizeof(uint16_t);
    770 		if (num > 0 &&
    771 		    find_cap(&rtic->strs, 's', ind) == NULL)
    772 		{
    773 			grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num);
    774 			le16enc(rtic->strs.buf + rtic->strs.bufpos, ind);
    775 			rtic->strs.bufpos += sizeof(uint16_t);
    776 			le16enc(rtic->strs.buf + rtic->strs.bufpos, num);
    777 			rtic->strs.bufpos += sizeof(uint16_t);
    778 			memcpy(rtic->strs.buf + rtic->strs.bufpos,
    779 			    cap, num);
    780 			rtic->strs.bufpos += num;
    781 			rtic->strs.entries++;
    782 		}
    783 		cap += num;
    784 	}
    785 
    786 	cap = utic->extras.buf;
    787 	for (n = utic->extras.entries; n > 0; n--) {
    788 		num = le16dec(cap);
    789 		cap += sizeof(uint16_t);
    790 		code = cap;
    791 		cap += num;
    792 		type = *cap++;
    793 		flag = 0;
    794 		str = NULL;
    795 		switch (type) {
    796 		case 'f':
    797 			flag = *cap++;
    798 			if (!VALID_BOOLEAN(flag))
    799 				continue;
    800 			break;
    801 		case 'n':
    802 			num = le16dec(cap);
    803 			cap += sizeof(uint16_t);
    804 			if (!VALID_NUMERIC(num))
    805 				continue;
    806 			break;
    807 		case 's':
    808 			num = le16dec(cap);
    809 			cap += sizeof(uint16_t);
    810 			str = cap;
    811 			cap += num;
    812 			if (num == 0)
    813 				continue;
    814 			break;
    815 		}
    816 		store_extra(rtic, 0, code, type, flag, num, str, num);
    817 	}
    818 }
    819 
    820 static size_t
    821 merge_use(void)
    822 {
    823 	size_t skipped, merged, memn;
    824 	char *cap, *scap;
    825 	uint16_t num;
    826 	TIC *rtic, *utic;
    827 	TERM *term, *uterm;;
    828 
    829 	skipped = merged = 0;
    830 	for (term = terms; term != NULL; term = term->next) {
    831 		if (term->type == 'a')
    832 			continue;
    833 		rtic = &term->tic;
    834 		while ((cap = find_extra(&rtic->extras, "use")) != NULL) {
    835 			if (*cap++ != 's') {
    836 				dowarn("%s: use is not string", rtic->name);
    837 				break;
    838 			}
    839 			cap += sizeof(uint16_t);
    840 			if (strcmp(rtic->name, cap) == 0) {
    841 				dowarn("%s: uses itself", rtic->name);
    842 				goto remove;
    843 			}
    844 			uterm = find_term(cap);
    845 			if (uterm != NULL && uterm->type == 'a')
    846 				uterm = find_term(uterm->tic.name);
    847 			if (uterm == NULL) {
    848 				dowarn("%s: no use record for %s",
    849 				    rtic->name, cap);
    850 				goto remove;
    851 			}
    852 			utic = &uterm->tic;
    853 			if (strcmp(utic->name, rtic->name) == 0) {
    854 				dowarn("%s: uses itself", rtic->name);
    855 				goto remove;
    856 			}
    857 			if (find_extra(&utic->extras, "use") != NULL) {
    858 				skipped++;
    859 				break;
    860 			}
    861 			cap = find_extra(&rtic->extras, "use");
    862 			merge(rtic, utic);
    863 	remove:
    864 			/* The pointers may have changed, find the use again */
    865 			cap = find_extra(&rtic->extras, "use");
    866 			if (cap == NULL)
    867 				dowarn("%s: use no longer exists - impossible",
    868 					rtic->name);
    869 			else {
    870 				scap = cap - (4 + sizeof(uint16_t));
    871 				cap++;
    872 				num = le16dec(cap);
    873 				cap += sizeof(uint16_t) + num;
    874 				memn = rtic->extras.bufpos -
    875 				    (cap - rtic->extras.buf);
    876 				memcpy(scap, cap, memn);
    877 				rtic->extras.bufpos -= cap - scap;
    878 				cap = scap;
    879 				rtic->extras.entries--;
    880 				merged++;
    881 			}
    882 		}
    883 	}
    884 
    885 	if (merged == 0 && skipped != 0)
    886 		dowarn("circular use detected");
    887 	return merged;
    888 }
    889 
    890 static int
    891 print_dump(int argc, char **argv)
    892 {
    893 	TERM *term;
    894 	TBUF *buf;
    895 	int i, n;
    896 	size_t j, col;
    897 
    898 	printf("struct compiled_term {\n");
    899 	printf("\tconst char *name;\n");
    900 	printf("\tconst char *cap;\n");
    901 	printf("\tsize_t caplen;\n");
    902 	printf("};\n\n");
    903 
    904 	printf("const struct compiled_term compiled_terms[] = {\n");
    905 
    906 	n = 0;
    907 	for (i = 0; i < argc; i++) {
    908 		term = find_term(argv[i]);
    909 		if (term == NULL) {
    910 			warnx("%s: no description for terminal", argv[i]);
    911 			continue;
    912 		}
    913 		if (term->type == 'a') {
    914 			warnx("%s: cannot dump alias", argv[i]);
    915 			continue;
    916 		}
    917 		buf = flatten_term(term);
    918 		if (buf == NULL)
    919 			continue;
    920 
    921 		printf("\t{\n");
    922 		printf("\t\t\"%s\",\n", argv[i]);
    923 		n++;
    924 		for (j = 0, col = 0; j < buf->bufpos; j++) {
    925 			if (col == 0) {
    926 				printf("\t\t\"");
    927 				col = 16;
    928 			}
    929 
    930 			col += printf("\\%03o", (uint8_t)buf->buf[j]);
    931 			if (col > 75) {
    932 				printf("\"%s\n",
    933 				    j + 1 == buf->bufpos ? "," : "");
    934 				col = 0;
    935 			}
    936 		}
    937 		if (col != 0)
    938 			printf("\",\n");
    939 		printf("\t\t%zu\n", buf->bufpos);
    940 		printf("\t}");
    941 		if (i + 1 < argc)
    942 			printf(",");
    943 		printf("\n");
    944 	}
    945 	printf("};\n");
    946 
    947 	return n;
    948 }
    949 
    950 int
    951 main(int argc, char **argv)
    952 {
    953 	int ch, cflag, sflag;
    954 	char *source, *p, *buf, *ofile;
    955 	FILE *f;
    956 	DBM *db;
    957 	size_t len, buflen, nterm, nalias;
    958 	TBUF tbuf;
    959 	TERM *term;
    960 
    961 	cflag = sflag = 0;
    962 	ofile = NULL;
    963 	while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
    964 	    switch (ch) {
    965 	    case 'S':
    966 		    Sflag = 1;
    967 		    break;
    968 	    case 'a':
    969 		    aflag = 1;
    970 		    break;
    971 	    case 'c':
    972 		    cflag = 1;
    973 		    break;
    974 	    case 'o':
    975 		    ofile = optarg;
    976 		    break;
    977 	    case 's':
    978 		    sflag = 1;
    979 		    break;
    980 	    case 'x':
    981 		    xflag = 1;
    982 		    break;
    983 	    case '?': /* FALLTHROUGH */
    984 	    default:
    985 		    fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
    986 			getprogname());
    987 		    return EXIT_FAILURE;
    988 	    }
    989 
    990 	if (optind == argc)
    991 		errx(1, "No source file given");
    992 	source = argv[optind++];
    993 	f = fopen(source, "r");
    994 	if (f == NULL)
    995 		err(1, "fopen: %s", source);
    996 	if (!cflag && !Sflag) {
    997 		if (ofile == NULL)
    998 			ofile = source;
    999 		len = strlen(ofile) + 9;
   1000 		dbname = malloc(len + 4); /* For adding .db after open */
   1001 		if (dbname == NULL)
   1002 			err(1, "malloc");
   1003 		snprintf(dbname, len, "%s.tmp", ofile);
   1004 		db = dbm_open(dbname, O_CREAT | O_RDWR | O_TRUNC, DEFFILEMODE);
   1005 		if (db == NULL)
   1006 			err(1, "dbopen: %s", source);
   1007 		p = dbname + strlen(dbname);
   1008 		*p++ = '.';
   1009 		*p++ = 'd';
   1010 		*p++ = 'b';
   1011 		*p++ = '\0';
   1012 		atexit(do_unlink);
   1013 	} else
   1014 		db = NULL; /* satisfy gcc warning */
   1015 
   1016 	tbuf.buflen = tbuf.bufpos = 0;
   1017 	while ((buf = fgetln(f, &buflen)) != NULL) {
   1018 		/* Skip comments */
   1019 		if (*buf == '#')
   1020 			continue;
   1021 		if (buf[buflen - 1] != '\n') {
   1022 			process_entry(&tbuf);
   1023 			dowarn("last line is not a comment"
   1024 			    " and does not end with a newline");
   1025 			continue;
   1026 		}
   1027 		/*
   1028 		  If the first char is space not a space then we have a
   1029 		  new entry, so process it.
   1030 		*/
   1031 		if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
   1032 			process_entry(&tbuf);
   1033 
   1034 		/* Grow the buffer if needed */
   1035 		grow_tbuf(&tbuf, buflen);
   1036 		/* Append the string */
   1037 		memcpy(tbuf.buf + tbuf.bufpos, buf, buflen);
   1038 		tbuf.bufpos += buflen;
   1039 	}
   1040 	/* Process the last entry if not done already */
   1041 	process_entry(&tbuf);
   1042 
   1043 	/* Merge use entries until we have merged all we can */
   1044 	while (merge_use() != 0)
   1045 		;
   1046 
   1047 	if (Sflag) {
   1048 		print_dump(argc - optind, argv + optind);
   1049 		return error_exit;
   1050 	}
   1051 
   1052 	if (cflag)
   1053 		return error_exit;
   1054 
   1055 	/* Save the terms */
   1056 	nterm = nalias = 0;
   1057 	for (term = terms; term != NULL; term = term->next) {
   1058 		save_term(db, term);
   1059 		if (term->type == 'a')
   1060 			nalias++;
   1061 		else
   1062 			nterm++;
   1063 	}
   1064 
   1065 	/* done! */
   1066 	dbm_close(db);
   1067 
   1068 	/* Rename the tmp db to the real one now */
   1069 	len = strlen(ofile) + 4;
   1070 	p = malloc(len);
   1071 	if (p == NULL)
   1072 		err(1, "malloc");
   1073 	snprintf(p, len, "%s.db", ofile);
   1074 	if (rename(dbname, p) == -1)
   1075 		err(1, "rename");
   1076 	free(dbname);
   1077 	dbname = NULL;
   1078 
   1079 	if (sflag != 0)
   1080 		fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
   1081 		    nterm, nalias, p);
   1082 
   1083 	return EXIT_SUCCESS;
   1084 }
   1085