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