Home | History | Annotate | Line # | Download | only in tic
tic.c revision 1.31.4.1
      1  1.31.4.1    martin /* $NetBSD: tic.c,v 1.31.4.1 2020/04/08 14:09:18 martin Exp $ */
      2       1.1       roy 
      3       1.1       roy /*
      4  1.31.4.1    martin  * Copyright (c) 2009, 2010, 2020 The NetBSD Foundation, Inc.
      5       1.1       roy  *
      6       1.1       roy  * This code is derived from software contributed to The NetBSD Foundation
      7       1.1       roy  * by Roy Marples.
      8       1.1       roy  *
      9       1.1       roy  * Redistribution and use in source and binary forms, with or without
     10       1.1       roy  * modification, are permitted provided that the following conditions
     11       1.1       roy  * are met:
     12       1.1       roy  * 1. Redistributions of source code must retain the above copyright
     13       1.1       roy  *    notice, this list of conditions and the following disclaimer.
     14       1.1       roy  * 2. Redistributions in binary form must reproduce the above copyright
     15       1.1       roy  *    notice, this list of conditions and the following disclaimer in the
     16       1.1       roy  *    documentation and/or other materials provided with the distribution.
     17       1.1       roy  *
     18       1.1       roy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19       1.1       roy  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20       1.1       roy  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21       1.1       roy  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22       1.1       roy  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23       1.1       roy  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24       1.1       roy  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25       1.1       roy  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26       1.1       roy  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27       1.1       roy  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28       1.1       roy  */
     29       1.1       roy 
     30       1.1       roy #if HAVE_NBTOOL_CONFIG_H
     31       1.1       roy #include "nbtool_config.h"
     32       1.1       roy #endif
     33       1.1       roy 
     34       1.1       roy #include <sys/cdefs.h>
     35  1.31.4.1    martin __RCSID("$NetBSD: tic.c,v 1.31.4.1 2020/04/08 14:09:18 martin Exp $");
     36       1.1       roy 
     37       1.1       roy #include <sys/types.h>
     38      1.14     joerg #include <sys/queue.h>
     39      1.27  christos #include <sys/stat.h>
     40       1.8  pgoyette 
     41       1.9  pgoyette #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
     42       1.7  pgoyette #include <sys/endian.h>
     43       1.8  pgoyette #endif
     44       1.1       roy 
     45      1.20     joerg #include <cdbw.h>
     46       1.1       roy #include <ctype.h>
     47       1.1       roy #include <err.h>
     48       1.1       roy #include <errno.h>
     49       1.1       roy #include <getopt.h>
     50       1.1       roy #include <limits.h>
     51       1.1       roy #include <fcntl.h>
     52      1.15     joerg #include <search.h>
     53       1.1       roy #include <stdarg.h>
     54  1.31.4.1    martin #include <stdbool.h>
     55       1.1       roy #include <stdlib.h>
     56       1.1       roy #include <stdio.h>
     57       1.1       roy #include <string.h>
     58       1.1       roy #include <term_private.h>
     59       1.1       roy #include <term.h>
     60      1.31     joerg #include <unistd.h>
     61      1.23     joerg #include <util.h>
     62      1.15     joerg 
     63      1.15     joerg #define	HASH_SIZE	16384	/* 2012-06-01: 3600 entries */
     64       1.1       roy 
     65       1.1       roy typedef struct term {
     66      1.20     joerg 	STAILQ_ENTRY(term) next;
     67       1.1       roy 	char *name;
     68      1.10       roy 	TIC *tic;
     69      1.20     joerg 	uint32_t id;
     70      1.20     joerg 	struct term *base_term;
     71       1.1       roy } TERM;
     72      1.20     joerg static STAILQ_HEAD(, term) terms = STAILQ_HEAD_INITIALIZER(terms);
     73       1.1       roy 
     74       1.1       roy static int error_exit;
     75      1.10       roy static int Sflag;
     76      1.18     joerg static size_t nterm, nalias;
     77       1.1       roy 
     78      1.13     joerg static void __printflike(1, 2)
     79       1.1       roy dowarn(const char *fmt, ...)
     80       1.1       roy {
     81       1.1       roy 	va_list va;
     82       1.1       roy 
     83       1.1       roy 	error_exit = 1;
     84       1.1       roy 	va_start(va, fmt);
     85       1.1       roy 	vwarnx(fmt, va);
     86       1.1       roy 	va_end(va);
     87       1.1       roy }
     88       1.1       roy 
     89       1.1       roy static char *
     90       1.1       roy grow_tbuf(TBUF *tbuf, size_t len)
     91       1.1       roy {
     92       1.1       roy 	char *buf;
     93       1.1       roy 
     94      1.10       roy 	buf = _ti_grow_tbuf(tbuf, len);
     95      1.10       roy 	if (buf == NULL)
     96  1.31.4.1    martin 		err(EXIT_FAILURE, "_ti_grow_tbuf");
     97      1.10       roy 	return buf;
     98       1.5       roy }
     99       1.5       roy 
    100       1.5       roy static int
    101      1.20     joerg save_term(struct cdbw *db, TERM *term)
    102       1.5       roy {
    103      1.10       roy 	uint8_t *buf;
    104      1.10       roy 	ssize_t len;
    105      1.20     joerg 	size_t slen = strlen(term->name) + 1;
    106      1.20     joerg 
    107      1.20     joerg 	if (term->base_term != NULL) {
    108  1.31.4.1    martin 		char *cap;
    109  1.31.4.1    martin 		len = (ssize_t)(1 + sizeof(uint32_t) + sizeof(uint16_t) + slen);
    110      1.20     joerg 		buf = emalloc(len);
    111  1.31.4.1    martin 		cap = (char *)buf;
    112  1.31.4.1    martin 		*cap++ = TERMINFO_ALIAS;
    113  1.31.4.1    martin 		_ti_encode_32(&cap, term->base_term->id);
    114  1.31.4.1    martin 		_ti_encode_count_str(&cap, term->name, slen);
    115      1.20     joerg 		if (cdbw_put(db, term->name, slen, buf, len))
    116  1.31.4.1    martin 			err(EXIT_FAILURE, "cdbw_put");
    117      1.26  christos 		free(buf);
    118      1.20     joerg 		return 0;
    119      1.20     joerg 	}
    120       1.5       roy 
    121      1.10       roy 	len = _ti_flatten(&buf, term->tic);
    122      1.10       roy 	if (len == -1)
    123       1.5       roy 		return -1;
    124       1.1       roy 
    125      1.20     joerg 	if (cdbw_put_data(db, buf, len, &term->id))
    126  1.31.4.1    martin 		err(EXIT_FAILURE, "cdbw_put_data");
    127      1.20     joerg 	if (cdbw_put_key(db, term->name, slen, term->id))
    128  1.31.4.1    martin 		err(EXIT_FAILURE, "cdbw_put_key");
    129      1.10       roy 	free(buf);
    130       1.1       roy 	return 0;
    131       1.1       roy }
    132       1.1       roy 
    133       1.1       roy static TERM *
    134       1.1       roy find_term(const char *name)
    135       1.1       roy {
    136      1.15     joerg 	ENTRY elem, *elemp;
    137      1.14     joerg 
    138      1.15     joerg 	elem.key = __UNCONST(name);
    139      1.15     joerg 	elem.data = NULL;
    140      1.15     joerg 	elemp = hsearch(elem, FIND);
    141      1.15     joerg 	return elemp ? (TERM *)elemp->data : NULL;
    142       1.1       roy }
    143       1.1       roy 
    144       1.1       roy static TERM *
    145  1.31.4.1    martin find_newest_term(const char *name)
    146  1.31.4.1    martin {
    147  1.31.4.1    martin 	char *lname;
    148  1.31.4.1    martin 	TERM *term;
    149  1.31.4.1    martin 
    150  1.31.4.1    martin 	lname = _ti_getname(TERMINFO_RTYPE, name);
    151  1.31.4.1    martin 	if (lname == NULL)
    152  1.31.4.1    martin 		return NULL;
    153  1.31.4.1    martin 	term = find_term(lname);
    154  1.31.4.1    martin 	free(lname);
    155  1.31.4.1    martin 	if (term == NULL)
    156  1.31.4.1    martin 		term = find_term(name);
    157  1.31.4.1    martin 	return term;
    158  1.31.4.1    martin }
    159  1.31.4.1    martin 
    160  1.31.4.1    martin static TERM *
    161      1.20     joerg store_term(const char *name, TERM *base_term)
    162       1.1       roy {
    163       1.1       roy 	TERM *term;
    164      1.15     joerg 	ENTRY elem;
    165       1.1       roy 
    166      1.16     joerg 	term = ecalloc(1, sizeof(*term));
    167      1.16     joerg 	term->name = estrdup(name);
    168      1.20     joerg 	STAILQ_INSERT_TAIL(&terms, term, next);
    169      1.15     joerg 	elem.key = estrdup(name);
    170      1.15     joerg 	elem.data = term;
    171      1.15     joerg 	hsearch(elem, ENTER);
    172      1.18     joerg 
    173      1.20     joerg 	term->base_term = base_term;
    174      1.20     joerg 	if (base_term != NULL)
    175      1.18     joerg 		nalias++;
    176      1.18     joerg 	else
    177      1.18     joerg 		nterm++;
    178      1.18     joerg 
    179       1.1       roy 	return term;
    180       1.1       roy }
    181       1.1       roy 
    182  1.31.4.1    martin static void
    183  1.31.4.1    martin alias_terms(TERM *term)
    184  1.31.4.1    martin {
    185  1.31.4.1    martin 	char *p, *e, *alias;
    186  1.31.4.1    martin 
    187  1.31.4.1    martin 	/* Create aliased terms */
    188  1.31.4.1    martin 	if (term->tic->alias == NULL)
    189  1.31.4.1    martin 		return;
    190  1.31.4.1    martin 
    191  1.31.4.1    martin 	alias = p = estrdup(term->tic->alias);
    192  1.31.4.1    martin 	while (p != NULL && *p != '\0') {
    193  1.31.4.1    martin 		e = strchr(p, '|');
    194  1.31.4.1    martin 		if (e != NULL)
    195  1.31.4.1    martin 			*e++ = '\0';
    196  1.31.4.1    martin 		/* No need to lengthcheck the alias because the main
    197  1.31.4.1    martin 		 * terminfo description already stores all the aliases
    198  1.31.4.1    martin 		 * in the same length field as the alias. */
    199  1.31.4.1    martin 		if (find_term(p) != NULL) {
    200  1.31.4.1    martin 			dowarn("%s: has alias for already assigned"
    201  1.31.4.1    martin 			    " term %s", term->tic->name, p);
    202  1.31.4.1    martin 		} else {
    203  1.31.4.1    martin 			store_term(p, term);
    204  1.31.4.1    martin 		}
    205  1.31.4.1    martin 		p = e;
    206  1.31.4.1    martin 	}
    207  1.31.4.1    martin 	free(alias);
    208  1.31.4.1    martin }
    209  1.31.4.1    martin 
    210       1.1       roy static int
    211      1.10       roy process_entry(TBUF *buf, int flags)
    212       1.1       roy {
    213       1.1       roy 	TERM *term;
    214       1.1       roy 	TIC *tic;
    215  1.31.4.1    martin 	TBUF sbuf = *buf;
    216      1.25       roy 
    217       1.1       roy 	if (buf->bufpos == 0)
    218       1.1       roy 		return 0;
    219       1.1       roy 	/* Terminate the string */
    220       1.1       roy 	buf->buf[buf->bufpos - 1] = '\0';
    221       1.1       roy 	/* First rewind the buffer for new entries */
    222       1.1       roy 	buf->bufpos = 0;
    223       1.1       roy 
    224       1.1       roy 	if (isspace((unsigned char)*buf->buf))
    225       1.1       roy 		return 0;
    226       1.1       roy 
    227      1.10       roy 	tic = _ti_compile(buf->buf, flags);
    228      1.10       roy 	if (tic == NULL)
    229       1.1       roy 		return 0;
    230      1.10       roy 
    231      1.10       roy 	if (find_term(tic->name) != NULL) {
    232      1.10       roy 		dowarn("%s: duplicate entry", tic->name);
    233      1.10       roy 		_ti_freetic(tic);
    234       1.1       roy 		return 0;
    235       1.1       roy 	}
    236      1.20     joerg 	term = store_term(tic->name, NULL);
    237      1.10       roy 	term->tic = tic;
    238  1.31.4.1    martin 	alias_terms(term);
    239       1.1       roy 
    240  1.31.4.1    martin 	if (tic->rtype == TERMINFO_RTYPE)
    241  1.31.4.1    martin 		return process_entry(&sbuf, flags | TIC_COMPAT_V1);
    242      1.25       roy 
    243       1.1       roy 	return 0;
    244       1.1       roy }
    245       1.1       roy 
    246       1.1       roy static void
    247      1.10       roy merge(TIC *rtic, TIC *utic, int flags)
    248       1.1       roy {
    249  1.31.4.1    martin 	char flag, type;
    250  1.31.4.1    martin 	const char *cap, *code, *str;
    251  1.31.4.1    martin 	short ind, len;
    252  1.31.4.1    martin 	int num;
    253       1.1       roy 	size_t n;
    254       1.1       roy 
    255  1.31.4.1    martin 	if (rtic->rtype < utic->rtype)
    256  1.31.4.1    martin 		errx(EXIT_FAILURE, "merge rtype diff (%s:%d into %s:%d)",
    257  1.31.4.1    martin 		    utic->name, utic->rtype, rtic->name, rtic->rtype);
    258  1.31.4.1    martin 
    259       1.1       roy 	cap = utic->flags.buf;
    260       1.1       roy 	for (n = utic->flags.entries; n > 0; n--) {
    261  1.31.4.1    martin 		ind = _ti_decode_16(&cap);
    262       1.1       roy 		flag = *cap++;
    263       1.1       roy 		if (VALID_BOOLEAN(flag) &&
    264  1.31.4.1    martin 		    _ti_find_cap(rtic, &rtic->flags, 'f', ind) == NULL)
    265       1.1       roy 		{
    266  1.31.4.1    martin 			if (!_ti_encode_buf_id_flags(&rtic->flags, ind, flag))
    267  1.31.4.1    martin 				err(EXIT_FAILURE, "encode flag");
    268       1.1       roy 		}
    269       1.1       roy 	}
    270       1.1       roy 
    271       1.1       roy 	cap = utic->nums.buf;
    272       1.1       roy 	for (n = utic->nums.entries; n > 0; n--) {
    273  1.31.4.1    martin 		ind = _ti_decode_16(&cap);
    274  1.31.4.1    martin 		num = _ti_decode_num(&cap, utic->rtype);
    275       1.1       roy 		if (VALID_NUMERIC(num) &&
    276  1.31.4.1    martin 		    _ti_find_cap(rtic, &rtic->nums, 'n', ind) == NULL)
    277       1.1       roy 		{
    278  1.31.4.1    martin 			if (!_ti_encode_buf_id_num(&rtic->nums, ind, num,
    279  1.31.4.1    martin 			    _ti_numsize(rtic)))
    280  1.31.4.1    martin 				err(EXIT_FAILURE, "encode num");
    281       1.1       roy 		}
    282       1.1       roy 	}
    283       1.1       roy 
    284       1.1       roy 	cap = utic->strs.buf;
    285       1.1       roy 	for (n = utic->strs.entries; n > 0; n--) {
    286  1.31.4.1    martin 		ind = _ti_decode_16(&cap);
    287  1.31.4.1    martin 		len = _ti_decode_16(&cap);
    288  1.31.4.1    martin 		if (len > 0 &&
    289  1.31.4.1    martin 		    _ti_find_cap(rtic, &rtic->strs, 's', ind) == NULL)
    290       1.1       roy 		{
    291  1.31.4.1    martin 			if (!_ti_encode_buf_id_count_str(&rtic->strs, ind, cap,
    292  1.31.4.1    martin 			    len))
    293  1.31.4.1    martin 				err(EXIT_FAILURE, "encode str");
    294       1.1       roy 		}
    295  1.31.4.1    martin 		cap += len;
    296       1.1       roy 	}
    297       1.1       roy 
    298       1.1       roy 	cap = utic->extras.buf;
    299       1.1       roy 	for (n = utic->extras.entries; n > 0; n--) {
    300  1.31.4.1    martin 		num = _ti_decode_16(&cap);
    301       1.1       roy 		code = cap;
    302       1.1       roy 		cap += num;
    303       1.1       roy 		type = *cap++;
    304       1.1       roy 		flag = 0;
    305       1.1       roy 		str = NULL;
    306       1.1       roy 		switch (type) {
    307       1.1       roy 		case 'f':
    308       1.1       roy 			flag = *cap++;
    309       1.1       roy 			if (!VALID_BOOLEAN(flag))
    310       1.1       roy 				continue;
    311       1.1       roy 			break;
    312       1.1       roy 		case 'n':
    313  1.31.4.1    martin 			num = _ti_decode_num(&cap, utic->rtype);
    314       1.1       roy 			if (!VALID_NUMERIC(num))
    315       1.1       roy 				continue;
    316       1.1       roy 			break;
    317       1.1       roy 		case 's':
    318  1.31.4.1    martin 			num = _ti_decode_16(&cap);
    319       1.1       roy 			str = cap;
    320       1.1       roy 			cap += num;
    321       1.1       roy 			if (num == 0)
    322       1.1       roy 				continue;
    323       1.1       roy 			break;
    324       1.1       roy 		}
    325      1.10       roy 		_ti_store_extra(rtic, 0, code, type, flag, num, str, num,
    326      1.10       roy 		    flags);
    327       1.1       roy 	}
    328       1.1       roy }
    329       1.1       roy 
    330  1.31.4.1    martin static int
    331  1.31.4.1    martin dup_tbuf(TBUF *dst, const TBUF *src)
    332  1.31.4.1    martin {
    333  1.31.4.1    martin 
    334  1.31.4.1    martin 	if (src->buflen == 0)
    335  1.31.4.1    martin 		return 0;
    336  1.31.4.1    martin 	dst->buf = malloc(src->buflen);
    337  1.31.4.1    martin 	if (dst->buf == NULL)
    338  1.31.4.1    martin 		return -1;
    339  1.31.4.1    martin 	dst->buflen = src->buflen;
    340  1.31.4.1    martin 	memcpy(dst->buf, src->buf, dst->buflen);
    341  1.31.4.1    martin 	dst->bufpos = src->bufpos;
    342  1.31.4.1    martin 	dst->entries = src->entries;
    343  1.31.4.1    martin 	return 0;
    344  1.31.4.1    martin }
    345  1.31.4.1    martin 
    346  1.31.4.1    martin static int
    347  1.31.4.1    martin promote(TIC *rtic, TIC *utic)
    348  1.31.4.1    martin {
    349  1.31.4.1    martin 	TERM *nrterm = find_newest_term(rtic->name);
    350  1.31.4.1    martin 	TERM *nuterm = find_newest_term(utic->name);
    351  1.31.4.1    martin 	TERM *term;
    352  1.31.4.1    martin 	TIC *tic;
    353  1.31.4.1    martin 
    354  1.31.4.1    martin 	if (nrterm == NULL || nuterm == NULL)
    355  1.31.4.1    martin 		return -1;
    356  1.31.4.1    martin 	if (nrterm->tic->rtype >= nuterm->tic->rtype)
    357  1.31.4.1    martin 		return 0;
    358  1.31.4.1    martin 
    359  1.31.4.1    martin 	tic = calloc(1, sizeof(*tic));
    360  1.31.4.1    martin 	if (tic == NULL)
    361  1.31.4.1    martin 		return -1;
    362  1.31.4.1    martin 
    363  1.31.4.1    martin 	tic->name = _ti_getname(TERMINFO_RTYPE, rtic->name);
    364  1.31.4.1    martin 	if (tic->name == NULL)
    365  1.31.4.1    martin 		goto err;
    366  1.31.4.1    martin 	if (rtic->alias != NULL) {
    367  1.31.4.1    martin 		tic->alias = strdup(rtic->alias);
    368  1.31.4.1    martin 		if (tic->alias == NULL)
    369  1.31.4.1    martin 			goto err;
    370  1.31.4.1    martin 	}
    371  1.31.4.1    martin 	if (rtic->desc != NULL) {
    372  1.31.4.1    martin 		tic->desc = strdup(rtic->desc);
    373  1.31.4.1    martin 		if (tic->desc == NULL)
    374  1.31.4.1    martin 			goto err;
    375  1.31.4.1    martin 	}
    376  1.31.4.1    martin 
    377  1.31.4.1    martin 	tic->rtype = rtic->rtype;
    378  1.31.4.1    martin 	if (dup_tbuf(&tic->flags, &rtic->flags) == -1)
    379  1.31.4.1    martin 		goto err;
    380  1.31.4.1    martin 	if (dup_tbuf(&tic->nums, &rtic->nums) == -1)
    381  1.31.4.1    martin 		goto err;
    382  1.31.4.1    martin 	if (dup_tbuf(&tic->strs, &rtic->strs) == -1)
    383  1.31.4.1    martin 		goto err;
    384  1.31.4.1    martin 	if (dup_tbuf(&tic->extras, &rtic->extras) == -1)
    385  1.31.4.1    martin 		goto err;
    386  1.31.4.1    martin 	if (_ti_promote(tic) == -1)
    387  1.31.4.1    martin 		goto err;
    388  1.31.4.1    martin 
    389  1.31.4.1    martin 	term = store_term(tic->name, NULL);
    390  1.31.4.1    martin 	if (term == NULL)
    391  1.31.4.1    martin 		goto err;
    392  1.31.4.1    martin 
    393  1.31.4.1    martin 	term->tic = tic;
    394  1.31.4.1    martin 	alias_terms(term);
    395  1.31.4.1    martin 	return 0;
    396  1.31.4.1    martin 
    397  1.31.4.1    martin err:
    398  1.31.4.1    martin 	free(tic->flags.buf);
    399  1.31.4.1    martin 	free(tic->nums.buf);
    400  1.31.4.1    martin 	free(tic->strs.buf);
    401  1.31.4.1    martin 	free(tic->extras.buf);
    402  1.31.4.1    martin 	free(tic->desc);
    403  1.31.4.1    martin 	free(tic->alias);
    404  1.31.4.1    martin 	free(tic->name);
    405  1.31.4.1    martin 	free(tic);
    406  1.31.4.1    martin 	return -1;
    407  1.31.4.1    martin }
    408  1.31.4.1    martin 
    409       1.1       roy static size_t
    410      1.10       roy merge_use(int flags)
    411       1.1       roy {
    412       1.1       roy 	size_t skipped, merged, memn;
    413  1.31.4.1    martin 	const char *cap;
    414  1.31.4.1    martin 	char *name, *basename;
    415       1.1       roy 	uint16_t num;
    416       1.1       roy 	TIC *rtic, *utic;
    417  1.31.4.1    martin 	TERM *term, *uterm;
    418  1.31.4.1    martin 	bool promoted;
    419       1.1       roy 
    420       1.1       roy 	skipped = merged = 0;
    421      1.20     joerg 	STAILQ_FOREACH(term, &terms, next) {
    422      1.20     joerg 		if (term->base_term != NULL)
    423       1.1       roy 			continue;
    424      1.10       roy 		rtic = term->tic;
    425  1.31.4.1    martin 		basename = _ti_getname(TERMINFO_RTYPE_O1, rtic->name);
    426  1.31.4.1    martin 		promoted = false;
    427  1.31.4.1    martin 		while ((cap = _ti_find_extra(rtic, &rtic->extras, "use"))
    428  1.31.4.1    martin 		    != NULL) {
    429       1.1       roy 			if (*cap++ != 's') {
    430       1.1       roy 				dowarn("%s: use is not string", rtic->name);
    431       1.1       roy 				break;
    432       1.1       roy 			}
    433       1.1       roy 			cap += sizeof(uint16_t);
    434  1.31.4.1    martin 			if (strcmp(basename, cap) == 0) {
    435       1.1       roy 				dowarn("%s: uses itself", rtic->name);
    436       1.1       roy 				goto remove;
    437       1.1       roy 			}
    438  1.31.4.1    martin 			name = _ti_getname(rtic->rtype, cap);
    439  1.31.4.1    martin 			if (name == NULL) {
    440  1.31.4.1    martin 				dowarn("%s: ???: %s", rtic->name, cap);
    441  1.31.4.1    martin 				goto remove;
    442  1.31.4.1    martin 			}
    443  1.31.4.1    martin 			uterm = find_term(name);
    444  1.31.4.1    martin 			free(name);
    445  1.31.4.1    martin 			if (uterm == NULL)
    446  1.31.4.1    martin 				uterm = find_term(cap);
    447      1.20     joerg 			if (uterm != NULL && uterm->base_term != NULL)
    448      1.20     joerg 				uterm = uterm->base_term;
    449       1.1       roy 			if (uterm == NULL) {
    450       1.1       roy 				dowarn("%s: no use record for %s",
    451       1.1       roy 				    rtic->name, cap);
    452       1.1       roy 				goto remove;
    453       1.1       roy 			}
    454      1.10       roy 			utic = uterm->tic;
    455       1.1       roy 			if (strcmp(utic->name, rtic->name) == 0) {
    456       1.1       roy 				dowarn("%s: uses itself", rtic->name);
    457       1.1       roy 				goto remove;
    458       1.1       roy 			}
    459  1.31.4.1    martin 			if (_ti_find_extra(utic, &utic->extras, "use")
    460  1.31.4.1    martin 			    != NULL) {
    461       1.1       roy 				skipped++;
    462       1.1       roy 				break;
    463       1.1       roy 			}
    464  1.31.4.1    martin 
    465  1.31.4.1    martin 			/* If we need to merge in a term that requires
    466  1.31.4.1    martin 			 * this term to be promoted, we need to duplicate
    467  1.31.4.1    martin 			 * this term, promote it and append it to our list. */
    468  1.31.4.1    martin 			if (!promoted && rtic->rtype != TERMINFO_RTYPE) {
    469  1.31.4.1    martin 				if (promote(rtic, utic) == -1)
    470  1.31.4.1    martin 					err(EXIT_FAILURE, "promote");
    471  1.31.4.1    martin 				promoted = true;
    472  1.31.4.1    martin 			}
    473  1.31.4.1    martin 
    474      1.10       roy 			merge(rtic, utic, flags);
    475       1.1       roy 	remove:
    476       1.1       roy 			/* The pointers may have changed, find the use again */
    477  1.31.4.1    martin 			cap = _ti_find_extra(rtic, &rtic->extras, "use");
    478       1.1       roy 			if (cap == NULL)
    479       1.1       roy 				dowarn("%s: use no longer exists - impossible",
    480       1.1       roy 					rtic->name);
    481       1.1       roy 			else {
    482  1.31.4.1    martin 				char *scap = __UNCONST(
    483  1.31.4.1    martin 				    cap - (4 + sizeof(uint16_t)));
    484       1.1       roy 				cap++;
    485  1.31.4.1    martin 				num = _ti_decode_16(&cap);
    486  1.31.4.1    martin 				cap += num;
    487       1.1       roy 				memn = rtic->extras.bufpos -
    488       1.1       roy 				    (cap - rtic->extras.buf);
    489      1.11       roy 				memmove(scap, cap, memn);
    490       1.1       roy 				rtic->extras.bufpos -= cap - scap;
    491       1.1       roy 				cap = scap;
    492       1.1       roy 				rtic->extras.entries--;
    493       1.1       roy 				merged++;
    494       1.1       roy 			}
    495       1.1       roy 		}
    496  1.31.4.1    martin 		free(basename);
    497       1.1       roy 	}
    498       1.1       roy 
    499       1.1       roy 	if (merged == 0 && skipped != 0)
    500       1.1       roy 		dowarn("circular use detected");
    501       1.1       roy 	return merged;
    502       1.1       roy }
    503       1.1       roy 
    504       1.5       roy static int
    505       1.5       roy print_dump(int argc, char **argv)
    506       1.5       roy {
    507       1.5       roy 	TERM *term;
    508      1.10       roy 	uint8_t *buf;
    509       1.5       roy 	int i, n;
    510       1.5       roy 	size_t j, col;
    511      1.10       roy 	ssize_t len;
    512       1.5       roy 
    513       1.6       roy 	printf("struct compiled_term {\n");
    514       1.6       roy 	printf("\tconst char *name;\n");
    515       1.6       roy 	printf("\tconst char *cap;\n");
    516       1.6       roy 	printf("\tsize_t caplen;\n");
    517       1.6       roy 	printf("};\n\n");
    518       1.6       roy 
    519       1.6       roy 	printf("const struct compiled_term compiled_terms[] = {\n");
    520       1.6       roy 
    521       1.5       roy 	n = 0;
    522       1.5       roy 	for (i = 0; i < argc; i++) {
    523  1.31.4.1    martin 		term = find_newest_term(argv[i]);
    524       1.5       roy 		if (term == NULL) {
    525       1.5       roy 			warnx("%s: no description for terminal", argv[i]);
    526       1.5       roy 			continue;
    527       1.5       roy 		}
    528      1.20     joerg 		if (term->base_term != NULL) {
    529       1.5       roy 			warnx("%s: cannot dump alias", argv[i]);
    530       1.5       roy 			continue;
    531       1.5       roy 		}
    532      1.10       roy 		/* Don't compile the aliases in, save space */
    533      1.10       roy 		free(term->tic->alias);
    534      1.10       roy 		term->tic->alias = NULL;
    535      1.10       roy 		len = _ti_flatten(&buf, term->tic);
    536      1.10       roy 		if (len == 0 || len == -1)
    537       1.5       roy 			continue;
    538       1.5       roy 
    539       1.6       roy 		printf("\t{\n");
    540       1.6       roy 		printf("\t\t\"%s\",\n", argv[i]);
    541       1.5       roy 		n++;
    542      1.10       roy 		for (j = 0, col = 0; j < (size_t)len; j++) {
    543       1.5       roy 			if (col == 0) {
    544       1.6       roy 				printf("\t\t\"");
    545       1.6       roy 				col = 16;
    546       1.5       roy 			}
    547      1.25       roy 
    548      1.10       roy 			col += printf("\\%03o", (uint8_t)buf[j]);
    549       1.5       roy 			if (col > 75) {
    550       1.5       roy 				printf("\"%s\n",
    551      1.10       roy 				    j + 1 == (size_t)len ? "," : "");
    552       1.5       roy 				col = 0;
    553       1.5       roy 			}
    554       1.5       roy 		}
    555       1.5       roy 		if (col != 0)
    556       1.6       roy 			printf("\",\n");
    557      1.10       roy 		printf("\t\t%zu\n", len);
    558       1.6       roy 		printf("\t}");
    559       1.6       roy 		if (i + 1 < argc)
    560       1.6       roy 			printf(",");
    561       1.6       roy 		printf("\n");
    562      1.10       roy 		free(buf);
    563       1.5       roy 	}
    564       1.6       roy 	printf("};\n");
    565       1.5       roy 
    566       1.5       roy 	return n;
    567       1.5       roy }
    568       1.5       roy 
    569      1.20     joerg static void
    570      1.20     joerg write_database(const char *dbname)
    571      1.20     joerg {
    572      1.20     joerg 	struct cdbw *db;
    573      1.20     joerg 	char *tmp_dbname;
    574      1.20     joerg 	TERM *term;
    575      1.20     joerg 	int fd;
    576      1.20     joerg 
    577      1.20     joerg 	db = cdbw_open();
    578      1.20     joerg 	if (db == NULL)
    579  1.31.4.1    martin 		err(EXIT_FAILURE, "cdbw_open failed");
    580      1.20     joerg 	/* Save the terms */
    581      1.20     joerg 	STAILQ_FOREACH(term, &terms, next)
    582      1.20     joerg 		save_term(db, term);
    583      1.20     joerg 
    584      1.20     joerg 	easprintf(&tmp_dbname, "%s.XXXXXX", dbname);
    585      1.20     joerg 	fd = mkstemp(tmp_dbname);
    586      1.20     joerg 	if (fd == -1)
    587  1.31.4.1    martin 		err(EXIT_FAILURE,
    588  1.31.4.1    martin 		    "creating temporary database %s failed", tmp_dbname);
    589      1.20     joerg 	if (cdbw_output(db, fd, "NetBSD terminfo", cdbw_stable_seeder))
    590  1.31.4.1    martin 		err(EXIT_FAILURE,
    591  1.31.4.1    martin 		    "writing temporary database %s failed", tmp_dbname);
    592      1.20     joerg 	if (fchmod(fd, DEFFILEMODE))
    593  1.31.4.1    martin 		err(EXIT_FAILURE, "fchmod failed");
    594      1.20     joerg 	if (close(fd))
    595  1.31.4.1    martin 		err(EXIT_FAILURE,
    596  1.31.4.1    martin 		    "writing temporary database %s failed", tmp_dbname);
    597      1.20     joerg 	if (rename(tmp_dbname, dbname))
    598  1.31.4.1    martin 		err(EXIT_FAILURE, "renaming %s to %s failed", tmp_dbname, dbname);
    599      1.20     joerg 	free(tmp_dbname);
    600      1.20     joerg 	cdbw_close(db);
    601      1.20     joerg }
    602      1.20     joerg 
    603       1.1       roy int
    604       1.1       roy main(int argc, char **argv)
    605       1.1       roy {
    606      1.10       roy 	int ch, cflag, sflag, flags;
    607      1.20     joerg 	char *source, *dbname, *buf, *ofile;
    608       1.1       roy 	FILE *f;
    609      1.18     joerg 	size_t buflen;
    610      1.12       roy 	ssize_t len;
    611       1.1       roy 	TBUF tbuf;
    612      1.29       roy 	struct term *term;
    613       1.1       roy 
    614       1.1       roy 	cflag = sflag = 0;
    615       1.1       roy 	ofile = NULL;
    616      1.10       roy 	flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING;
    617       1.5       roy 	while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
    618       1.1       roy 	    switch (ch) {
    619       1.5       roy 	    case 'S':
    620       1.5       roy 		    Sflag = 1;
    621      1.10       roy 		    /* We still compile aliases so that use= works.
    622      1.10       roy 		     * However, it's removed before we flatten to save space. */
    623      1.10       roy 		    flags &= ~TIC_DESCRIPTION;
    624       1.5       roy 		    break;
    625       1.1       roy 	    case 'a':
    626      1.10       roy 		    flags |= TIC_COMMENT;
    627       1.1       roy 		    break;
    628       1.1       roy 	    case 'c':
    629       1.4       roy 		    cflag = 1;
    630       1.1       roy 		    break;
    631       1.1       roy 	    case 'o':
    632       1.1       roy 		    ofile = optarg;
    633       1.1       roy 		    break;
    634       1.1       roy 	    case 's':
    635       1.4       roy 		    sflag = 1;
    636       1.1       roy 		    break;
    637       1.1       roy 	    case 'x':
    638      1.10       roy 		    flags |= TIC_EXTRA;
    639       1.1       roy 		    break;
    640       1.1       roy 	    case '?': /* FALLTHROUGH */
    641       1.1       roy 	    default:
    642       1.6       roy 		    fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
    643       1.1       roy 			getprogname());
    644       1.1       roy 		    return EXIT_FAILURE;
    645       1.1       roy 	    }
    646       1.1       roy 
    647       1.1       roy 	if (optind == argc)
    648       1.1       roy 		errx(1, "No source file given");
    649       1.1       roy 	source = argv[optind++];
    650       1.1       roy 	f = fopen(source, "r");
    651       1.1       roy 	if (f == NULL)
    652  1.31.4.1    martin 		err(EXIT_FAILURE, "fopen: %s", source);
    653       1.1       roy 
    654      1.15     joerg 	hcreate(HASH_SIZE);
    655      1.15     joerg 
    656      1.19     joerg 	buf = tbuf.buf = NULL;
    657      1.28       roy 	buflen = tbuf.buflen = tbuf.bufpos = 0;
    658      1.12       roy 	while ((len = getline(&buf, &buflen, f)) != -1) {
    659       1.1       roy 		/* Skip comments */
    660       1.1       roy 		if (*buf == '#')
    661       1.1       roy 			continue;
    662      1.12       roy 		if (buf[len - 1] != '\n') {
    663      1.10       roy 			process_entry(&tbuf, flags);
    664       1.1       roy 			dowarn("last line is not a comment"
    665       1.1       roy 			    " and does not end with a newline");
    666       1.1       roy 			continue;
    667       1.1       roy 		}
    668       1.1       roy 		/*
    669      1.28       roy 		 * If the first char is space not a space then we have a
    670      1.28       roy 		 * new entry, so process it.
    671      1.28       roy 		 */
    672       1.1       roy 		if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
    673      1.10       roy 			process_entry(&tbuf, flags);
    674      1.25       roy 
    675       1.1       roy 		/* Grow the buffer if needed */
    676      1.12       roy 		grow_tbuf(&tbuf, len);
    677       1.1       roy 		/* Append the string */
    678      1.12       roy 		memcpy(tbuf.buf + tbuf.bufpos, buf, len);
    679      1.12       roy 		tbuf.bufpos += len;
    680       1.1       roy 	}
    681      1.19     joerg 	free(buf);
    682       1.1       roy 	/* Process the last entry if not done already */
    683      1.10       roy 	process_entry(&tbuf, flags);
    684      1.19     joerg 	free(tbuf.buf);
    685       1.1       roy 
    686       1.1       roy 	/* Merge use entries until we have merged all we can */
    687      1.10       roy 	while (merge_use(flags) != 0)
    688       1.1       roy 		;
    689       1.1       roy 
    690       1.6       roy 	if (Sflag) {
    691       1.5       roy 		print_dump(argc - optind, argv + optind);
    692       1.5       roy 		return error_exit;
    693       1.5       roy 	}
    694       1.5       roy 
    695       1.6       roy 	if (cflag)
    696       1.1       roy 		return error_exit;
    697      1.18     joerg 
    698      1.20     joerg 	if (ofile == NULL)
    699      1.20     joerg 		easprintf(&dbname, "%s.cdb", source);
    700      1.20     joerg 	else
    701      1.20     joerg 		dbname = ofile;
    702      1.20     joerg 	write_database(dbname);
    703       1.1       roy 
    704       1.1       roy 	if (sflag != 0)
    705       1.1       roy 		fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
    706      1.20     joerg 		    nterm, nalias, dbname);
    707      1.19     joerg 
    708      1.20     joerg 	if (ofile == NULL)
    709      1.20     joerg 		free(dbname);
    710      1.20     joerg 	while ((term = STAILQ_FIRST(&terms)) != NULL) {
    711      1.20     joerg 		STAILQ_REMOVE_HEAD(&terms, next);
    712      1.19     joerg 		_ti_freetic(term->tic);
    713      1.19     joerg 		free(term->name);
    714      1.19     joerg 		free(term);
    715      1.19     joerg 	}
    716      1.30  christos #ifndef HAVE_NBTOOL_CONFIG_H
    717      1.30  christos 	/*
    718      1.30  christos 	 * hdestroy1 is not standard but we don't really care if we
    719      1.30  christos 	 * leak in the tools version
    720      1.30  christos 	 */
    721      1.24  christos 	hdestroy1(free, NULL);
    722      1.30  christos #endif
    723       1.1       roy 
    724       1.1       roy 	return EXIT_SUCCESS;
    725       1.1       roy }
    726