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