xref: /netbsd-src/usr.bin/tic/tic.c (revision 98b3419359483c2c49c815cae40f57a582deb119)
1*98b34193Schristos /* $NetBSD: tic.c,v 1.42 2024/05/20 14:41:37 christos Exp $ */
24ca00e00Sroy 
34ca00e00Sroy /*
4aadfdb11Sroy  * Copyright (c) 2009, 2010, 2020 The NetBSD Foundation, Inc.
54ca00e00Sroy  *
64ca00e00Sroy  * This code is derived from software contributed to The NetBSD Foundation
74ca00e00Sroy  * by Roy Marples.
84ca00e00Sroy  *
94ca00e00Sroy  * Redistribution and use in source and binary forms, with or without
104ca00e00Sroy  * modification, are permitted provided that the following conditions
114ca00e00Sroy  * are met:
124ca00e00Sroy  * 1. Redistributions of source code must retain the above copyright
134ca00e00Sroy  *    notice, this list of conditions and the following disclaimer.
144ca00e00Sroy  * 2. Redistributions in binary form must reproduce the above copyright
154ca00e00Sroy  *    notice, this list of conditions and the following disclaimer in the
164ca00e00Sroy  *    documentation and/or other materials provided with the distribution.
174ca00e00Sroy  *
184ca00e00Sroy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
194ca00e00Sroy  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
204ca00e00Sroy  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
214ca00e00Sroy  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
224ca00e00Sroy  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
234ca00e00Sroy  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
244ca00e00Sroy  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
254ca00e00Sroy  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
264ca00e00Sroy  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
274ca00e00Sroy  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
284ca00e00Sroy  */
294ca00e00Sroy 
304ca00e00Sroy #if HAVE_NBTOOL_CONFIG_H
314ca00e00Sroy #include "nbtool_config.h"
324ca00e00Sroy #endif
334ca00e00Sroy 
344ca00e00Sroy #include <sys/cdefs.h>
35*98b34193Schristos __RCSID("$NetBSD: tic.c,v 1.42 2024/05/20 14:41:37 christos Exp $");
364ca00e00Sroy 
374ca00e00Sroy #include <sys/types.h>
38974fdf37Sjoerg #include <sys/queue.h>
396dbf86bcSchristos #include <sys/stat.h>
404b2f1808Spgoyette 
413360c946Spgoyette #if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
42237e56a5Spgoyette #include <sys/endian.h>
434b2f1808Spgoyette #endif
444ca00e00Sroy 
45fc67b0cdSjoerg #include <cdbw.h>
464ca00e00Sroy #include <ctype.h>
474ca00e00Sroy #include <err.h>
484ca00e00Sroy #include <errno.h>
494ca00e00Sroy #include <getopt.h>
504ca00e00Sroy #include <limits.h>
514ca00e00Sroy #include <fcntl.h>
522b5ab407Sjoerg #include <search.h>
534ca00e00Sroy #include <stdarg.h>
547acb5614Sroy #include <stdbool.h>
554ca00e00Sroy #include <stdlib.h>
564ca00e00Sroy #include <stdio.h>
574ca00e00Sroy #include <string.h>
584ca00e00Sroy #include <term_private.h>
594ca00e00Sroy #include <term.h>
602b691b87Sjoerg #include <unistd.h>
61d6b0aba1Sjoerg #include <util.h>
622b5ab407Sjoerg 
632b5ab407Sjoerg #define	HASH_SIZE	16384	/* 2012-06-01: 3600 entries */
644ca00e00Sroy 
654ca00e00Sroy typedef struct term {
66fc67b0cdSjoerg 	STAILQ_ENTRY(term) next;
674ca00e00Sroy 	char *name;
68fde317d2Sroy 	TIC *tic;
69fc67b0cdSjoerg 	uint32_t id;
70fc67b0cdSjoerg 	struct term *base_term;
714ca00e00Sroy } TERM;
72fc67b0cdSjoerg static STAILQ_HEAD(, term) terms = STAILQ_HEAD_INITIALIZER(terms);
734ca00e00Sroy 
744ca00e00Sroy static int error_exit;
75fde317d2Sroy static int Sflag;
767b1c18b0Sjoerg static size_t nterm, nalias;
774ca00e00Sroy 
78968a666eSjoerg static void __printflike(1, 2)
dowarn(const char * fmt,...)794ca00e00Sroy dowarn(const char *fmt, ...)
804ca00e00Sroy {
814ca00e00Sroy 	va_list va;
824ca00e00Sroy 
834ca00e00Sroy 	error_exit = 1;
844ca00e00Sroy 	va_start(va, fmt);
854ca00e00Sroy 	vwarnx(fmt, va);
864ca00e00Sroy 	va_end(va);
874ca00e00Sroy }
884ca00e00Sroy 
894ca00e00Sroy static char *
grow_tbuf(TBUF * tbuf,size_t len)904ca00e00Sroy grow_tbuf(TBUF *tbuf, size_t len)
914ca00e00Sroy {
924ca00e00Sroy 	char *buf;
934ca00e00Sroy 
94fde317d2Sroy 	buf = _ti_grow_tbuf(tbuf, len);
954ca00e00Sroy 	if (buf == NULL)
96d58481dbSroy 		err(EXIT_FAILURE, "_ti_grow_tbuf");
97fde317d2Sroy 	return buf;
9855a396b9Sroy }
9955a396b9Sroy 
10055a396b9Sroy static int
save_term(struct cdbw * db,TERM * term)101fc67b0cdSjoerg save_term(struct cdbw *db, TERM *term)
10255a396b9Sroy {
103fde317d2Sroy 	uint8_t *buf;
104fde317d2Sroy 	ssize_t len;
105fc67b0cdSjoerg 	size_t slen = strlen(term->name) + 1;
106fc67b0cdSjoerg 
107fc67b0cdSjoerg 	if (term->base_term != NULL) {
108f73231daSchristos 		char *cap;
109f73231daSchristos 		len = (ssize_t)(1 + sizeof(uint32_t) + sizeof(uint16_t) + slen);
110fc67b0cdSjoerg 		buf = emalloc(len);
111f73231daSchristos 		cap = (char *)buf;
112f73231daSchristos 		*cap++ = TERMINFO_ALIAS;
113f73231daSchristos 		_ti_encode_32(&cap, term->base_term->id);
114f73231daSchristos 		_ti_encode_count_str(&cap, term->name, slen);
115fc67b0cdSjoerg 		if (cdbw_put(db, term->name, slen, buf, len))
116d58481dbSroy 			err(EXIT_FAILURE, "cdbw_put");
1179896a45fSchristos 		free(buf);
118fc67b0cdSjoerg 		return 0;
119fc67b0cdSjoerg 	}
12055a396b9Sroy 
121fde317d2Sroy 	len = _ti_flatten(&buf, term->tic);
122fde317d2Sroy 	if (len == -1)
12355a396b9Sroy 		return -1;
1244ca00e00Sroy 
125fc67b0cdSjoerg 	if (cdbw_put_data(db, buf, len, &term->id))
126d58481dbSroy 		err(EXIT_FAILURE, "cdbw_put_data");
127fc67b0cdSjoerg 	if (cdbw_put_key(db, term->name, slen, term->id))
128d58481dbSroy 		err(EXIT_FAILURE, "cdbw_put_key");
129fde317d2Sroy 	free(buf);
1304ca00e00Sroy 	return 0;
1314ca00e00Sroy }
1324ca00e00Sroy 
1334ca00e00Sroy static TERM *
find_term(const char * name)1344ca00e00Sroy find_term(const char *name)
1354ca00e00Sroy {
1362b5ab407Sjoerg 	ENTRY elem, *elemp;
1374ca00e00Sroy 
1382b5ab407Sjoerg 	elem.key = __UNCONST(name);
1392b5ab407Sjoerg 	elem.data = NULL;
1402b5ab407Sjoerg 	elemp = hsearch(elem, FIND);
1412b5ab407Sjoerg 	return elemp ? (TERM *)elemp->data : NULL;
1424ca00e00Sroy }
1434ca00e00Sroy 
1444ca00e00Sroy static TERM *
find_newest_term(const char * name)1457acb5614Sroy find_newest_term(const char *name)
1467acb5614Sroy {
1477acb5614Sroy 	char *lname;
1487acb5614Sroy 	TERM *term;
1497acb5614Sroy 
1507acb5614Sroy 	lname = _ti_getname(TERMINFO_RTYPE, name);
1517acb5614Sroy 	if (lname == NULL)
1527acb5614Sroy 		return NULL;
1537acb5614Sroy 	term = find_term(lname);
1547acb5614Sroy 	free(lname);
1557acb5614Sroy 	if (term == NULL)
1567acb5614Sroy 		term = find_term(name);
1577acb5614Sroy 	return term;
1587acb5614Sroy }
1597acb5614Sroy 
1607acb5614Sroy static TERM *
store_term(const char * name,TERM * base_term)161fc67b0cdSjoerg store_term(const char *name, TERM *base_term)
1624ca00e00Sroy {
1634ca00e00Sroy 	TERM *term;
1642b5ab407Sjoerg 	ENTRY elem;
1654ca00e00Sroy 
166cff379a9Sjoerg 	term = ecalloc(1, sizeof(*term));
167cff379a9Sjoerg 	term->name = estrdup(name);
168fc67b0cdSjoerg 	STAILQ_INSERT_TAIL(&terms, term, next);
1692b5ab407Sjoerg 	elem.key = estrdup(name);
1702b5ab407Sjoerg 	elem.data = term;
1712b5ab407Sjoerg 	hsearch(elem, ENTER);
1727b1c18b0Sjoerg 
173fc67b0cdSjoerg 	term->base_term = base_term;
174fc67b0cdSjoerg 	if (base_term != NULL)
1757b1c18b0Sjoerg 		nalias++;
1767b1c18b0Sjoerg 	else
1777b1c18b0Sjoerg 		nterm++;
1787b1c18b0Sjoerg 
1794ca00e00Sroy 	return term;
1804ca00e00Sroy }
1814ca00e00Sroy 
1827f0204b5Sroy static void
alias_terms(TERM * term)1837f0204b5Sroy alias_terms(TERM *term)
1847f0204b5Sroy {
1857f0204b5Sroy 	char *p, *e, *alias;
1867f0204b5Sroy 
1877f0204b5Sroy 	/* Create aliased terms */
1887f0204b5Sroy 	if (term->tic->alias == NULL)
1897f0204b5Sroy 		return;
1907f0204b5Sroy 
1917f0204b5Sroy 	alias = p = estrdup(term->tic->alias);
1927f0204b5Sroy 	while (p != NULL && *p != '\0') {
1937f0204b5Sroy 		e = strchr(p, '|');
1947f0204b5Sroy 		if (e != NULL)
1957f0204b5Sroy 			*e++ = '\0';
1967f0204b5Sroy 		/* No need to lengthcheck the alias because the main
1977f0204b5Sroy 		 * terminfo description already stores all the aliases
1987f0204b5Sroy 		 * in the same length field as the alias. */
1997f0204b5Sroy 		if (find_term(p) != NULL) {
2007f0204b5Sroy 			dowarn("%s: has alias for already assigned"
2017f0204b5Sroy 			    " term %s", term->tic->name, p);
2027f0204b5Sroy 		} else {
2037f0204b5Sroy 			store_term(p, term);
2047f0204b5Sroy 		}
2057f0204b5Sroy 		p = e;
2067f0204b5Sroy 	}
2077f0204b5Sroy 	free(alias);
2087f0204b5Sroy }
2097f0204b5Sroy 
2104ca00e00Sroy static int
process_entry(TBUF * buf,int flags)211fde317d2Sroy process_entry(TBUF *buf, int flags)
2124ca00e00Sroy {
2134ca00e00Sroy 	TERM *term;
2144ca00e00Sroy 	TIC *tic;
2153958d16aSchristos 	TBUF sbuf = *buf;
2164ca00e00Sroy 
2174ca00e00Sroy 	if (buf->bufpos == 0)
2184ca00e00Sroy 		return 0;
2194ca00e00Sroy 	/* Terminate the string */
2204ca00e00Sroy 	buf->buf[buf->bufpos - 1] = '\0';
2214ca00e00Sroy 	/* First rewind the buffer for new entries */
2224ca00e00Sroy 	buf->bufpos = 0;
2234ca00e00Sroy 
2244ca00e00Sroy 	if (isspace((unsigned char)*buf->buf))
2254ca00e00Sroy 		return 0;
2264ca00e00Sroy 
227fde317d2Sroy 	tic = _ti_compile(buf->buf, flags);
228fde317d2Sroy 	if (tic == NULL)
2294ca00e00Sroy 		return 0;
2304ca00e00Sroy 
231fde317d2Sroy 	if (find_term(tic->name) != NULL) {
232fde317d2Sroy 		dowarn("%s: duplicate entry", tic->name);
233fde317d2Sroy 		_ti_freetic(tic);
2344ca00e00Sroy 		return 0;
2354ca00e00Sroy 	}
236fc67b0cdSjoerg 	term = store_term(tic->name, NULL);
237fde317d2Sroy 	term->tic = tic;
2387f0204b5Sroy 	alias_terms(term);
2394ca00e00Sroy 
2403958d16aSchristos 	if (tic->rtype == TERMINFO_RTYPE)
2413958d16aSchristos 		return process_entry(&sbuf, flags | TIC_COMPAT_V1);
2423958d16aSchristos 
2434ca00e00Sroy 	return 0;
2444ca00e00Sroy }
2454ca00e00Sroy 
2464ca00e00Sroy static void
merge(TIC * rtic,TIC * utic,int flags)247fde317d2Sroy merge(TIC *rtic, TIC *utic, int flags)
2484ca00e00Sroy {
2493958d16aSchristos 	char flag, type;
2503958d16aSchristos 	const char *cap, *code, *str;
251aadfdb11Sroy 	short ind, len;
252aadfdb11Sroy 	int num;
2534ca00e00Sroy 	size_t n;
2544ca00e00Sroy 
2557acb5614Sroy 	if (rtic->rtype < utic->rtype)
2567acb5614Sroy 		errx(EXIT_FAILURE, "merge rtype diff (%s:%d into %s:%d)",
2577acb5614Sroy 		    utic->name, utic->rtype, rtic->name, rtic->rtype);
258f15baa5bSroy 
2594ca00e00Sroy 	cap = utic->flags.buf;
2604ca00e00Sroy 	for (n = utic->flags.entries; n > 0; n--) {
261f73231daSchristos 		ind = _ti_decode_16(&cap);
2624ca00e00Sroy 		flag = *cap++;
2634ca00e00Sroy 		if (VALID_BOOLEAN(flag) &&
2643958d16aSchristos 		    _ti_find_cap(rtic, &rtic->flags, 'f', ind) == NULL)
2654ca00e00Sroy 		{
266f73231daSchristos 			if (!_ti_encode_buf_id_flags(&rtic->flags, ind, flag))
267d58481dbSroy 				err(EXIT_FAILURE, "encode flag");
2684ca00e00Sroy 		}
2694ca00e00Sroy 	}
2704ca00e00Sroy 
2714ca00e00Sroy 	cap = utic->nums.buf;
2724ca00e00Sroy 	for (n = utic->nums.entries; n > 0; n--) {
273f73231daSchristos 		ind = _ti_decode_16(&cap);
274f73231daSchristos 		num = _ti_decode_num(&cap, utic->rtype);
2754ca00e00Sroy 		if (VALID_NUMERIC(num) &&
2763958d16aSchristos 		    _ti_find_cap(rtic, &rtic->nums, 'n', ind) == NULL)
2774ca00e00Sroy 		{
278f73231daSchristos 			if (!_ti_encode_buf_id_num(&rtic->nums, ind, num,
279f73231daSchristos 			    _ti_numsize(rtic)))
280d58481dbSroy 				err(EXIT_FAILURE, "encode num");
2814ca00e00Sroy 		}
2824ca00e00Sroy 	}
2834ca00e00Sroy 
2844ca00e00Sroy 	cap = utic->strs.buf;
2854ca00e00Sroy 	for (n = utic->strs.entries; n > 0; n--) {
286f73231daSchristos 		ind = _ti_decode_16(&cap);
287f73231daSchristos 		len = _ti_decode_16(&cap);
288aadfdb11Sroy 		if (len > 0 &&
2893958d16aSchristos 		    _ti_find_cap(rtic, &rtic->strs, 's', ind) == NULL)
2904ca00e00Sroy 		{
291f73231daSchristos 			if (!_ti_encode_buf_id_count_str(&rtic->strs, ind, cap,
292f73231daSchristos 			    len))
293d58481dbSroy 				err(EXIT_FAILURE, "encode str");
2944ca00e00Sroy 		}
295aadfdb11Sroy 		cap += len;
2964ca00e00Sroy 	}
2974ca00e00Sroy 
2984ca00e00Sroy 	cap = utic->extras.buf;
2994ca00e00Sroy 	for (n = utic->extras.entries; n > 0; n--) {
300f73231daSchristos 		num = _ti_decode_16(&cap);
3014ca00e00Sroy 		code = cap;
3024ca00e00Sroy 		cap += num;
3034ca00e00Sroy 		type = *cap++;
3044ca00e00Sroy 		flag = 0;
3054ca00e00Sroy 		str = NULL;
3064ca00e00Sroy 		switch (type) {
3074ca00e00Sroy 		case 'f':
3084ca00e00Sroy 			flag = *cap++;
3094ca00e00Sroy 			if (!VALID_BOOLEAN(flag))
3104ca00e00Sroy 				continue;
3114ca00e00Sroy 			break;
3124ca00e00Sroy 		case 'n':
313f73231daSchristos 			num = _ti_decode_num(&cap, utic->rtype);
3144ca00e00Sroy 			if (!VALID_NUMERIC(num))
3154ca00e00Sroy 				continue;
3164ca00e00Sroy 			break;
3174ca00e00Sroy 		case 's':
318f73231daSchristos 			num = _ti_decode_16(&cap);
3194ca00e00Sroy 			str = cap;
3204ca00e00Sroy 			cap += num;
3214ca00e00Sroy 			if (num == 0)
3224ca00e00Sroy 				continue;
3234ca00e00Sroy 			break;
3244ca00e00Sroy 		}
325fde317d2Sroy 		_ti_store_extra(rtic, 0, code, type, flag, num, str, num,
326fde317d2Sroy 		    flags);
3274ca00e00Sroy 	}
3284ca00e00Sroy }
3294ca00e00Sroy 
3307acb5614Sroy static int
dup_tbuf(TBUF * dst,const TBUF * src)3317acb5614Sroy dup_tbuf(TBUF *dst, const TBUF *src)
3327acb5614Sroy {
3337acb5614Sroy 
3347acb5614Sroy 	if (src->buflen == 0)
3357acb5614Sroy 		return 0;
3367acb5614Sroy 	dst->buf = malloc(src->buflen);
3377acb5614Sroy 	if (dst->buf == NULL)
3387acb5614Sroy 		return -1;
3397acb5614Sroy 	dst->buflen = src->buflen;
3407acb5614Sroy 	memcpy(dst->buf, src->buf, dst->buflen);
3417acb5614Sroy 	dst->bufpos = src->bufpos;
3427acb5614Sroy 	dst->entries = src->entries;
3437acb5614Sroy 	return 0;
3447acb5614Sroy }
3457acb5614Sroy 
3467acb5614Sroy static int
promote(TIC * rtic,TIC * utic)3477acb5614Sroy promote(TIC *rtic, TIC *utic)
3487acb5614Sroy {
3497acb5614Sroy 	TERM *nrterm = find_newest_term(rtic->name);
3507acb5614Sroy 	TERM *nuterm = find_newest_term(utic->name);
3517acb5614Sroy 	TERM *term;
3527acb5614Sroy 	TIC *tic;
3537acb5614Sroy 
3547acb5614Sroy 	if (nrterm == NULL || nuterm == NULL)
3557acb5614Sroy 		return -1;
3567acb5614Sroy 	if (nrterm->tic->rtype >= nuterm->tic->rtype)
3577acb5614Sroy 		return 0;
3587acb5614Sroy 
3597acb5614Sroy 	tic = calloc(1, sizeof(*tic));
3607acb5614Sroy 	if (tic == NULL)
3617acb5614Sroy 		return -1;
3627acb5614Sroy 
3637acb5614Sroy 	tic->name = _ti_getname(TERMINFO_RTYPE, rtic->name);
3647acb5614Sroy 	if (tic->name == NULL)
3657acb5614Sroy 		goto err;
3667f0204b5Sroy 	if (rtic->alias != NULL) {
3677f0204b5Sroy 		tic->alias = strdup(rtic->alias);
3687f0204b5Sroy 		if (tic->alias == NULL)
3697f0204b5Sroy 			goto err;
3707f0204b5Sroy 	}
3717acb5614Sroy 	if (rtic->desc != NULL) {
3727acb5614Sroy 		tic->desc = strdup(rtic->desc);
3737acb5614Sroy 		if (tic->desc == NULL)
3747acb5614Sroy 			goto err;
3757acb5614Sroy 	}
3767acb5614Sroy 
3777acb5614Sroy 	tic->rtype = rtic->rtype;
3787acb5614Sroy 	if (dup_tbuf(&tic->flags, &rtic->flags) == -1)
3797acb5614Sroy 		goto err;
3807acb5614Sroy 	if (dup_tbuf(&tic->nums, &rtic->nums) == -1)
3817acb5614Sroy 		goto err;
3827acb5614Sroy 	if (dup_tbuf(&tic->strs, &rtic->strs) == -1)
3837acb5614Sroy 		goto err;
3847acb5614Sroy 	if (dup_tbuf(&tic->extras, &rtic->extras) == -1)
3857acb5614Sroy 		goto err;
3867acb5614Sroy 	if (_ti_promote(tic) == -1)
3877acb5614Sroy 		goto err;
3887acb5614Sroy 
3897acb5614Sroy 	term = store_term(tic->name, NULL);
3907f0204b5Sroy 	if (term == NULL)
3917f0204b5Sroy 		goto err;
3927f0204b5Sroy 
3937acb5614Sroy 	term->tic = tic;
3947f0204b5Sroy 	alias_terms(term);
3957acb5614Sroy 	return 0;
3967acb5614Sroy 
3977acb5614Sroy err:
3987acb5614Sroy 	free(tic->flags.buf);
3997acb5614Sroy 	free(tic->nums.buf);
4007acb5614Sroy 	free(tic->strs.buf);
4017acb5614Sroy 	free(tic->extras.buf);
4027acb5614Sroy 	free(tic->desc);
4037f0204b5Sroy 	free(tic->alias);
4047acb5614Sroy 	free(tic->name);
4057acb5614Sroy 	free(tic);
4067acb5614Sroy 	return -1;
4077acb5614Sroy }
4087acb5614Sroy 
4094ca00e00Sroy static size_t
merge_use(int flags)410fde317d2Sroy merge_use(int flags)
4114ca00e00Sroy {
4124ca00e00Sroy 	size_t skipped, merged, memn;
413f73231daSchristos 	const char *cap;
4147acb5614Sroy 	char *name, *basename;
4154ca00e00Sroy 	uint16_t num;
4164ca00e00Sroy 	TIC *rtic, *utic;
4177acb5614Sroy 	TERM *term, *uterm;
4187acb5614Sroy 	bool promoted;
4194ca00e00Sroy 
4204ca00e00Sroy 	skipped = merged = 0;
421fc67b0cdSjoerg 	STAILQ_FOREACH(term, &terms, next) {
422fc67b0cdSjoerg 		if (term->base_term != NULL)
4234ca00e00Sroy 			continue;
424fde317d2Sroy 		rtic = term->tic;
4257acb5614Sroy 		basename = _ti_getname(TERMINFO_RTYPE_O1, rtic->name);
4267acb5614Sroy 		promoted = false;
4273958d16aSchristos 		while ((cap = _ti_find_extra(rtic, &rtic->extras, "use"))
4283958d16aSchristos 		    != NULL) {
4294ca00e00Sroy 			if (*cap++ != 's') {
4304ca00e00Sroy 				dowarn("%s: use is not string", rtic->name);
4314ca00e00Sroy 				break;
4324ca00e00Sroy 			}
4334ca00e00Sroy 			cap += sizeof(uint16_t);
4347acb5614Sroy 			if (strcmp(basename, cap) == 0) {
4354ca00e00Sroy 				dowarn("%s: uses itself", rtic->name);
4364ca00e00Sroy 				goto remove;
4374ca00e00Sroy 			}
4387acb5614Sroy 			name = _ti_getname(rtic->rtype, cap);
4397acb5614Sroy 			if (name == NULL) {
4407acb5614Sroy 				dowarn("%s: ???: %s", rtic->name, cap);
4417acb5614Sroy 				goto remove;
4427acb5614Sroy 			}
4437acb5614Sroy 			uterm = find_term(name);
4447acb5614Sroy 			free(name);
4457acb5614Sroy 			if (uterm == NULL)
4464ca00e00Sroy 				uterm = find_term(cap);
447fc67b0cdSjoerg 			if (uterm != NULL && uterm->base_term != NULL)
448fc67b0cdSjoerg 				uterm = uterm->base_term;
4494ca00e00Sroy 			if (uterm == NULL) {
4504ca00e00Sroy 				dowarn("%s: no use record for %s",
4514ca00e00Sroy 				    rtic->name, cap);
4524ca00e00Sroy 				goto remove;
4534ca00e00Sroy 			}
454fde317d2Sroy 			utic = uterm->tic;
4554ca00e00Sroy 			if (strcmp(utic->name, rtic->name) == 0) {
4564ca00e00Sroy 				dowarn("%s: uses itself", rtic->name);
4574ca00e00Sroy 				goto remove;
4584ca00e00Sroy 			}
4593958d16aSchristos 			if (_ti_find_extra(utic, &utic->extras, "use")
4603958d16aSchristos 			    != NULL) {
4614ca00e00Sroy 				skipped++;
4624ca00e00Sroy 				break;
4634ca00e00Sroy 			}
4647acb5614Sroy 
4657acb5614Sroy 			/* If we need to merge in a term that requires
4667acb5614Sroy 			 * this term to be promoted, we need to duplicate
4677acb5614Sroy 			 * this term, promote it and append it to our list. */
4687acb5614Sroy 			if (!promoted && rtic->rtype != TERMINFO_RTYPE) {
4697acb5614Sroy 				if (promote(rtic, utic) == -1)
4707acb5614Sroy 					err(EXIT_FAILURE, "promote");
471a3cbce75Schristos 				promoted = rtic->rtype == TERMINFO_RTYPE;
4727acb5614Sroy 			}
4737acb5614Sroy 
474fde317d2Sroy 			merge(rtic, utic, flags);
4754ca00e00Sroy 	remove:
4764ca00e00Sroy 			/* The pointers may have changed, find the use again */
4773958d16aSchristos 			cap = _ti_find_extra(rtic, &rtic->extras, "use");
4784ca00e00Sroy 			if (cap == NULL)
4794ca00e00Sroy 				dowarn("%s: use no longer exists - impossible",
4804ca00e00Sroy 					rtic->name);
4814ca00e00Sroy 			else {
482f73231daSchristos 				char *scap = __UNCONST(
483f73231daSchristos 				    cap - (4 + sizeof(uint16_t)));
4844ca00e00Sroy 				cap++;
485f73231daSchristos 				num = _ti_decode_16(&cap);
486f73231daSchristos 				cap += num;
4874ca00e00Sroy 				memn = rtic->extras.bufpos -
4884ca00e00Sroy 				    (cap - rtic->extras.buf);
489ff103979Sroy 				memmove(scap, cap, memn);
4904ca00e00Sroy 				rtic->extras.bufpos -= cap - scap;
4914ca00e00Sroy 				cap = scap;
4924ca00e00Sroy 				rtic->extras.entries--;
4934ca00e00Sroy 				merged++;
4944ca00e00Sroy 			}
4954ca00e00Sroy 		}
4967acb5614Sroy 		free(basename);
4974ca00e00Sroy 	}
4984ca00e00Sroy 
4994ca00e00Sroy 	if (merged == 0 && skipped != 0)
5004ca00e00Sroy 		dowarn("circular use detected");
5014ca00e00Sroy 	return merged;
5024ca00e00Sroy }
5034ca00e00Sroy 
50455a396b9Sroy static int
print_dump(int argc,char ** argv)50555a396b9Sroy print_dump(int argc, char **argv)
50655a396b9Sroy {
50755a396b9Sroy 	TERM *term;
508fde317d2Sroy 	uint8_t *buf;
50955a396b9Sroy 	int i, n;
51055a396b9Sroy 	size_t j, col;
511fde317d2Sroy 	ssize_t len;
51255a396b9Sroy 
513b164f98aSroy 	printf("struct compiled_term {\n");
514b164f98aSroy 	printf("\tconst char *name;\n");
515b164f98aSroy 	printf("\tconst char *cap;\n");
516b164f98aSroy 	printf("\tsize_t caplen;\n");
517b164f98aSroy 	printf("};\n\n");
51855a396b9Sroy 
519b164f98aSroy 	printf("const struct compiled_term compiled_terms[] = {\n");
520b164f98aSroy 
521b164f98aSroy 	n = 0;
52255a396b9Sroy 	for (i = 0; i < argc; i++) {
5237acb5614Sroy 		term = find_newest_term(argv[i]);
52455a396b9Sroy 		if (term == NULL) {
52555a396b9Sroy 			warnx("%s: no description for terminal", argv[i]);
52655a396b9Sroy 			continue;
52755a396b9Sroy 		}
528fc67b0cdSjoerg 		if (term->base_term != NULL) {
52955a396b9Sroy 			warnx("%s: cannot dump alias", argv[i]);
53055a396b9Sroy 			continue;
53155a396b9Sroy 		}
532fde317d2Sroy 		/* Don't compile the aliases in, save space */
533fde317d2Sroy 		free(term->tic->alias);
534fde317d2Sroy 		term->tic->alias = NULL;
535fde317d2Sroy 		len = _ti_flatten(&buf, term->tic);
536fde317d2Sroy 		if (len == 0 || len == -1)
53755a396b9Sroy 			continue;
53855a396b9Sroy 
539b164f98aSroy 		printf("\t{\n");
540b164f98aSroy 		printf("\t\t\"%s\",\n", argv[i]);
54155a396b9Sroy 		n++;
542fde317d2Sroy 		for (j = 0, col = 0; j < (size_t)len; j++) {
54355a396b9Sroy 			if (col == 0) {
544b164f98aSroy 				printf("\t\t\"");
545b164f98aSroy 				col = 16;
54655a396b9Sroy 			}
54755a396b9Sroy 
548fde317d2Sroy 			col += printf("\\%03o", (uint8_t)buf[j]);
54955a396b9Sroy 			if (col > 75) {
55055a396b9Sroy 				printf("\"%s\n",
551fde317d2Sroy 				    j + 1 == (size_t)len ? "," : "");
55255a396b9Sroy 				col = 0;
55355a396b9Sroy 			}
55455a396b9Sroy 		}
55555a396b9Sroy 		if (col != 0)
55655a396b9Sroy 			printf("\",\n");
557fde317d2Sroy 		printf("\t\t%zu\n", len);
558b164f98aSroy 		printf("\t}");
559b164f98aSroy 		if (i + 1 < argc)
560b164f98aSroy 			printf(",");
561b164f98aSroy 		printf("\n");
562fde317d2Sroy 		free(buf);
56355a396b9Sroy 	}
564b164f98aSroy 	printf("};\n");
56555a396b9Sroy 
56655a396b9Sroy 	return n;
56755a396b9Sroy }
56855a396b9Sroy 
569fc67b0cdSjoerg static void
write_database(const char * dbname)570fc67b0cdSjoerg write_database(const char *dbname)
571fc67b0cdSjoerg {
572fc67b0cdSjoerg 	struct cdbw *db;
573fc67b0cdSjoerg 	char *tmp_dbname;
574fc67b0cdSjoerg 	TERM *term;
575fc67b0cdSjoerg 	int fd;
576*98b34193Schristos 	mode_t m;
577fc67b0cdSjoerg 
578fc67b0cdSjoerg 	db = cdbw_open();
579fc67b0cdSjoerg 	if (db == NULL)
580d58481dbSroy 		err(EXIT_FAILURE, "cdbw_open failed");
581fc67b0cdSjoerg 	/* Save the terms */
582fc67b0cdSjoerg 	STAILQ_FOREACH(term, &terms, next)
583fc67b0cdSjoerg 		save_term(db, term);
584fc67b0cdSjoerg 
585fc67b0cdSjoerg 	easprintf(&tmp_dbname, "%s.XXXXXX", dbname);
586fc67b0cdSjoerg 	fd = mkstemp(tmp_dbname);
587fc67b0cdSjoerg 	if (fd == -1)
588d58481dbSroy 		err(EXIT_FAILURE,
589d58481dbSroy 		    "creating temporary database %s failed", tmp_dbname);
590fc67b0cdSjoerg 	if (cdbw_output(db, fd, "NetBSD terminfo", cdbw_stable_seeder))
591d58481dbSroy 		err(EXIT_FAILURE,
592d58481dbSroy 		    "writing temporary database %s failed", tmp_dbname);
593*98b34193Schristos 	m = umask(0);
594*98b34193Schristos 	(void)umask(m);
595*98b34193Schristos 	if (fchmod(fd, DEFFILEMODE & ~m))
596d58481dbSroy 		err(EXIT_FAILURE, "fchmod failed");
597fc67b0cdSjoerg 	if (close(fd))
598d58481dbSroy 		err(EXIT_FAILURE,
599d58481dbSroy 		    "writing temporary database %s failed", tmp_dbname);
600fc67b0cdSjoerg 	if (rename(tmp_dbname, dbname))
601d58481dbSroy 		err(EXIT_FAILURE, "renaming %s to %s failed", tmp_dbname, dbname);
602fc67b0cdSjoerg 	free(tmp_dbname);
603fc67b0cdSjoerg 	cdbw_close(db);
604fc67b0cdSjoerg }
605fc67b0cdSjoerg 
6064ca00e00Sroy int
main(int argc,char ** argv)6074ca00e00Sroy main(int argc, char **argv)
6084ca00e00Sroy {
609fde317d2Sroy 	int ch, cflag, sflag, flags;
610fc67b0cdSjoerg 	char *source, *dbname, *buf, *ofile;
6114ca00e00Sroy 	FILE *f;
6127b1c18b0Sjoerg 	size_t buflen;
6136a222e71Sroy 	ssize_t len;
6144ca00e00Sroy 	TBUF tbuf;
61555c5fc25Sroy 	struct term *term;
6164ca00e00Sroy 
6174ca00e00Sroy 	cflag = sflag = 0;
6184ca00e00Sroy 	ofile = NULL;
619fde317d2Sroy 	flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING;
62055a396b9Sroy 	while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
6214ca00e00Sroy 	    switch (ch) {
62255a396b9Sroy 	    case 'S':
62355a396b9Sroy 		    Sflag = 1;
624fde317d2Sroy 		    /* We still compile aliases so that use= works.
625fde317d2Sroy 		     * However, it's removed before we flatten to save space. */
626fde317d2Sroy 		    flags &= ~TIC_DESCRIPTION;
62755a396b9Sroy 		    break;
6284ca00e00Sroy 	    case 'a':
629fde317d2Sroy 		    flags |= TIC_COMMENT;
6304ca00e00Sroy 		    break;
6314ca00e00Sroy 	    case 'c':
63271dfbe1cSroy 		    cflag = 1;
6334ca00e00Sroy 		    break;
6344ca00e00Sroy 	    case 'o':
6354ca00e00Sroy 		    ofile = optarg;
6364ca00e00Sroy 		    break;
6374ca00e00Sroy 	    case 's':
63871dfbe1cSroy 		    sflag = 1;
6394ca00e00Sroy 		    break;
6404ca00e00Sroy 	    case 'x':
641fde317d2Sroy 		    flags |= TIC_EXTRA;
6424ca00e00Sroy 		    break;
6434ca00e00Sroy 	    case '?': /* FALLTHROUGH */
6444ca00e00Sroy 	    default:
645b164f98aSroy 		    fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
6464ca00e00Sroy 			getprogname());
6474ca00e00Sroy 		    return EXIT_FAILURE;
6484ca00e00Sroy 	    }
6494ca00e00Sroy 
6504ca00e00Sroy 	if (optind == argc)
6514ca00e00Sroy 		errx(1, "No source file given");
6524ca00e00Sroy 	source = argv[optind++];
6534ca00e00Sroy 	f = fopen(source, "r");
6544ca00e00Sroy 	if (f == NULL)
655d58481dbSroy 		err(EXIT_FAILURE, "fopen: %s", source);
6564ca00e00Sroy 
6572b5ab407Sjoerg 	hcreate(HASH_SIZE);
6582b5ab407Sjoerg 
65968a65e1aSjoerg 	buf = tbuf.buf = NULL;
6606a222e71Sroy 	buflen = tbuf.buflen = tbuf.bufpos = 0;
6616a222e71Sroy 	while ((len = getline(&buf, &buflen, f)) != -1) {
6624ca00e00Sroy 		/* Skip comments */
6634ca00e00Sroy 		if (*buf == '#')
6644ca00e00Sroy 			continue;
6656a222e71Sroy 		if (buf[len - 1] != '\n') {
666fde317d2Sroy 			process_entry(&tbuf, flags);
6674ca00e00Sroy 			dowarn("last line is not a comment"
6684ca00e00Sroy 			    " and does not end with a newline");
6694ca00e00Sroy 			continue;
6704ca00e00Sroy 		}
6714ca00e00Sroy 		/*
672e04b9c57Sroy 		 * If the first char is space not a space then we have a
673e04b9c57Sroy 		 * new entry, so process it.
6744ca00e00Sroy 		 */
6754ca00e00Sroy 		if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
676fde317d2Sroy 			process_entry(&tbuf, flags);
6774ca00e00Sroy 
6784ca00e00Sroy 		/* Grow the buffer if needed */
6796a222e71Sroy 		grow_tbuf(&tbuf, len);
6804ca00e00Sroy 		/* Append the string */
6816a222e71Sroy 		memcpy(tbuf.buf + tbuf.bufpos, buf, len);
6826a222e71Sroy 		tbuf.bufpos += len;
6834ca00e00Sroy 	}
68468a65e1aSjoerg 	free(buf);
6854ca00e00Sroy 	/* Process the last entry if not done already */
686fde317d2Sroy 	process_entry(&tbuf, flags);
68768a65e1aSjoerg 	free(tbuf.buf);
6884ca00e00Sroy 
6894ca00e00Sroy 	/* Merge use entries until we have merged all we can */
690fde317d2Sroy 	while (merge_use(flags) != 0)
6914ca00e00Sroy 		;
6924ca00e00Sroy 
693b164f98aSroy 	if (Sflag) {
69455a396b9Sroy 		print_dump(argc - optind, argv + optind);
69555a396b9Sroy 		return error_exit;
69655a396b9Sroy 	}
69755a396b9Sroy 
698b164f98aSroy 	if (cflag)
6994ca00e00Sroy 		return error_exit;
7004ca00e00Sroy 
701fc67b0cdSjoerg 	if (ofile == NULL)
702fc67b0cdSjoerg 		easprintf(&dbname, "%s.cdb", source);
703fc67b0cdSjoerg 	else
704fc67b0cdSjoerg 		dbname = ofile;
705fc67b0cdSjoerg 	write_database(dbname);
7064ca00e00Sroy 
7074ca00e00Sroy 	if (sflag != 0)
7084ca00e00Sroy 		fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
709fc67b0cdSjoerg 		    nterm, nalias, dbname);
7104ca00e00Sroy 
711fc67b0cdSjoerg 	if (ofile == NULL)
712fc67b0cdSjoerg 		free(dbname);
713fc67b0cdSjoerg 	while ((term = STAILQ_FIRST(&terms)) != NULL) {
714fc67b0cdSjoerg 		STAILQ_REMOVE_HEAD(&terms, next);
71568a65e1aSjoerg 		_ti_freetic(term->tic);
71668a65e1aSjoerg 		free(term->name);
71768a65e1aSjoerg 		free(term);
71868a65e1aSjoerg 	}
7199a21bc93Schristos #ifndef HAVE_NBTOOL_CONFIG_H
7209a21bc93Schristos 	/*
7219a21bc93Schristos 	 * hdestroy1 is not standard but we don't really care if we
7229a21bc93Schristos 	 * leak in the tools version
7239a21bc93Schristos 	 */
724ed61af43Schristos 	hdestroy1(free, NULL);
7259a21bc93Schristos #endif
72668a65e1aSjoerg 
7274ca00e00Sroy 	return EXIT_SUCCESS;
7284ca00e00Sroy }
729