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