xref: /netbsd/usr.bin/infocmp/infocmp.c (revision fe2a18ef)
1*fe2a18efSroy /* $NetBSD: infocmp.c,v 1.17 2020/03/31 12:44:15 roy Exp $ */
24ca00e00Sroy 
34ca00e00Sroy /*
4c33983b9Sroy  * 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 #include <sys/cdefs.h>
31*fe2a18efSroy __RCSID("$NetBSD: infocmp.c,v 1.17 2020/03/31 12:44:15 roy Exp $");
324ca00e00Sroy 
334ca00e00Sroy #include <sys/ioctl.h>
344ca00e00Sroy 
354ca00e00Sroy #include <ctype.h>
364ca00e00Sroy #include <err.h>
374ca00e00Sroy #include <stdio.h>
384ca00e00Sroy #include <stdlib.h>
394ca00e00Sroy #include <string.h>
404ca00e00Sroy #include <term_private.h>
414ca00e00Sroy #include <term.h>
424ca00e00Sroy #include <unistd.h>
430fca9bd2Schristos #include <util.h>
444ca00e00Sroy 
454ca00e00Sroy #define SW 8
464ca00e00Sroy 
474ca00e00Sroy typedef struct tient {
484ca00e00Sroy 	char type;
494ca00e00Sroy 	const char *id;
50461cb40bSroy 	signed char flag;
51c33983b9Sroy 	int num;
524ca00e00Sroy 	const char *str;
534ca00e00Sroy } TIENT;
544ca00e00Sroy 
554ca00e00Sroy static size_t cols;
565f630912Sroy static int aflag, cflag, nflag, qflag, xflag;
574ca00e00Sroy 
584ca00e00Sroy static size_t
outstr(FILE * f,const char * str)594ca00e00Sroy outstr(FILE *f, const char *str)
604ca00e00Sroy {
614ca00e00Sroy 	unsigned char ch;
624ca00e00Sroy 	size_t r, l;
634ca00e00Sroy 
644ca00e00Sroy 	r = 0;
654ca00e00Sroy 	l = strlen(str);
664ca00e00Sroy 	while ((ch = (unsigned char)(*str++)) != '\0') {
674ca00e00Sroy 		switch (ch) {
684ca00e00Sroy 		case 128:
694ca00e00Sroy 			ch = '0';
704ca00e00Sroy 			break;
714ca00e00Sroy 		case '\033':
724ca00e00Sroy 			ch = 'E';
734ca00e00Sroy 			break;
744ca00e00Sroy 		case '\014':
754ca00e00Sroy 			ch = 'f';
764ca00e00Sroy 			break;
774ca00e00Sroy 		case '^': /* FALLTHROUGH */
784ca00e00Sroy 		case ',': /* escape these */
794ca00e00Sroy 			break;
804ca00e00Sroy 		case ' ':
814ca00e00Sroy 			ch = 's';
824ca00e00Sroy 			break;
834ca00e00Sroy 		default:
844ca00e00Sroy 			if (ch == '\177') {
854ca00e00Sroy 				if (f != NULL)
864ca00e00Sroy 					fputc('^', f);
874ca00e00Sroy 				ch = '?';
884ca00e00Sroy 				r++;
894ca00e00Sroy 			} else if (iscntrl(ch) &&
904ca00e00Sroy 			    ch < 128 &&
914ca00e00Sroy 			    ch != '\\' &&
924ca00e00Sroy 			    (l < 4 || isdigit((unsigned char)*str)))
934ca00e00Sroy 			{
944ca00e00Sroy 				if (f != NULL)
954ca00e00Sroy 					fputc('^', f);
964ca00e00Sroy 				ch += '@';
974ca00e00Sroy 				r++;
984ca00e00Sroy 			} else if (!isprint(ch)) {
994ca00e00Sroy 				if (f != NULL)
1004ca00e00Sroy 					fprintf(f, "\\%03o", ch);
1014ca00e00Sroy 				r += 4;
1024ca00e00Sroy 				continue;
1034ca00e00Sroy 			}
1044ca00e00Sroy 			goto prnt;
1054ca00e00Sroy 		}
1064ca00e00Sroy 
1074ca00e00Sroy 		if (f != NULL)
1084ca00e00Sroy 			fputc('\\', f);
1094ca00e00Sroy 		r++;
1104ca00e00Sroy prnt:
1114ca00e00Sroy 		if (f != NULL)
1124ca00e00Sroy 			fputc(ch, f);
1134ca00e00Sroy 		r++;
1144ca00e00Sroy 	}
1154ca00e00Sroy 	return r;
1164ca00e00Sroy }
1174ca00e00Sroy 
1184ca00e00Sroy static int
ent_compare(const void * a,const void * b)1194ca00e00Sroy ent_compare(const void *a, const void *b)
1204ca00e00Sroy {
1214ca00e00Sroy 	const TIENT *ta, *tb;
1224ca00e00Sroy 
1234ca00e00Sroy 	ta = (const TIENT *)a;
1244ca00e00Sroy 	tb = (const TIENT *)b;
1254ca00e00Sroy 	return strcmp(ta->id, tb->id);
1264ca00e00Sroy }
1274ca00e00Sroy 
1284ca00e00Sroy static void
setdb(char * db)1294ca00e00Sroy setdb(char *db)
1304ca00e00Sroy {
1312f3310bfSchristos 	static const char *ext[] = { ".cdb", ".db" };
1324ca00e00Sroy 
1332f3310bfSchristos 	for (size_t i = 0; i < __arraycount(ext); i++) {
1342f3310bfSchristos 		char *ptr = strstr(db, ext[i]);
1352f3310bfSchristos 		if (ptr == NULL || ptr[strlen(ext[i])] != '\0')
1362f3310bfSchristos 			continue;
1372f3310bfSchristos 		*ptr = '\0';
1382f3310bfSchristos 		break;
1392f3310bfSchristos 	}
1404ca00e00Sroy 	setenv("TERMINFO", db, 1);
1414ca00e00Sroy }
1424ca00e00Sroy 
1434ca00e00Sroy static void
print_ent(const TIENT * ents,size_t nents)1444ca00e00Sroy print_ent(const TIENT *ents, size_t nents)
1454ca00e00Sroy {
1464ca00e00Sroy 	size_t col, i, l;
1474ca00e00Sroy 	char nbuf[64];
1484ca00e00Sroy 
1494ca00e00Sroy 	if (nents == 0)
1504ca00e00Sroy 		return;
1514ca00e00Sroy 
1524ca00e00Sroy 	col = SW;
1534ca00e00Sroy 	printf("\t");
1544ca00e00Sroy 	for (i = 0; i < nents; i++) {
1554ca00e00Sroy 		if (*ents[i].id == '.' && aflag == 0)
1564ca00e00Sroy 			continue;
1574ca00e00Sroy 		switch (ents[i].type) {
1584ca00e00Sroy 		case 'f':
1594ca00e00Sroy 			if (ents[i].flag == ABSENT_BOOLEAN)
1604ca00e00Sroy 				continue;
1614ca00e00Sroy 			l = strlen(ents[i].id) + 2;
1624ca00e00Sroy 			if (ents[i].flag == CANCELLED_BOOLEAN)
1634ca00e00Sroy 				l++;
1644ca00e00Sroy 			break;
1654ca00e00Sroy 		case 'n':
1664ca00e00Sroy 			if (ents[i].num == ABSENT_NUMERIC)
1674ca00e00Sroy 				continue;
1684ca00e00Sroy 			if (VALID_NUMERIC(ents[i].num))
1694ca00e00Sroy 				l = snprintf(nbuf, sizeof(nbuf), "%s#%d,",
1704ca00e00Sroy 				    ents[i].id, ents[i].num);
1714ca00e00Sroy 			else
1724ca00e00Sroy 				l = snprintf(nbuf, sizeof(nbuf), "%s@,",
1734ca00e00Sroy 				    ents[i].id);
1744ca00e00Sroy 			break;
1754ca00e00Sroy 		case 's':
1764ca00e00Sroy 			if (ents[i].str == ABSENT_STRING)
1774ca00e00Sroy 				continue;
1784ca00e00Sroy 			if (VALID_STRING(ents[i].str))
1794ca00e00Sroy 				l = strlen(ents[i].id) +
1804ca00e00Sroy 				    outstr(NULL, ents[i].str) + 7;
1814ca00e00Sroy 			else
1824ca00e00Sroy 				l = strlen(ents[i].id) + 3;
1834ca00e00Sroy 			break;
1844ca00e00Sroy 		default:
1850fca9bd2Schristos 			errx(EXIT_FAILURE, "invalid type");
1864ca00e00Sroy 		}
1874ca00e00Sroy 		if (col != SW) {
1884ca00e00Sroy 			if (col + l > cols) {
1894ca00e00Sroy 				printf("\n\t");
1904ca00e00Sroy 				col = SW;
1914ca00e00Sroy 			} else
1924ca00e00Sroy 				col += printf(" ");
1934ca00e00Sroy 		}
1944ca00e00Sroy 		switch (ents[i].type) {
1954ca00e00Sroy 		case 'f':
1964ca00e00Sroy 			col += printf("%s", ents[i].id);
1974ca00e00Sroy 			if (ents[i].flag == ABSENT_BOOLEAN ||
1984ca00e00Sroy 			    ents[i].flag == CANCELLED_BOOLEAN)
1994ca00e00Sroy 				col += printf("@");
2004ca00e00Sroy 			col += printf(",");
2014ca00e00Sroy 			break;
2024ca00e00Sroy 		case 'n':
2034ca00e00Sroy 			col += printf("%s", nbuf);
2044ca00e00Sroy 			break;
2054ca00e00Sroy 		case 's':
2064ca00e00Sroy 			col += printf("%s", ents[i].id);
2074ca00e00Sroy 			if (VALID_STRING(ents[i].str)) {
2084ca00e00Sroy 				col += printf("=");
2094ca00e00Sroy 				col += outstr(stdout, ents[i].str);
2104ca00e00Sroy 			} else
2114ca00e00Sroy 				col += printf("@");
2124ca00e00Sroy 			col += printf(",");
2134ca00e00Sroy 			break;
2144ca00e00Sroy 		}
2154ca00e00Sroy 	}
2164ca00e00Sroy 	printf("\n");
2174ca00e00Sroy }
2184ca00e00Sroy 
2194ca00e00Sroy static size_t
load_ents(TIENT * ents,TERMINAL * t,char type)2204ca00e00Sroy load_ents(TIENT *ents, TERMINAL *t, char type)
2214ca00e00Sroy {
2224ca00e00Sroy 	size_t i, n, max;
2234ca00e00Sroy 	TERMUSERDEF *ud;
2244ca00e00Sroy 
2254ca00e00Sroy 	switch (type) {
2264ca00e00Sroy 	case 'f':
2274ca00e00Sroy 		max = TIFLAGMAX;
2284ca00e00Sroy 		break;
2294ca00e00Sroy 	case 'n':
2304ca00e00Sroy 		max = TINUMMAX;
2314ca00e00Sroy 		break;
2324ca00e00Sroy 	default:
2334ca00e00Sroy 		max = TISTRMAX;
2344ca00e00Sroy 	}
2354ca00e00Sroy 
2364ca00e00Sroy 	n = 0;
2374ca00e00Sroy 	for (i = 0; i <= max; i++) {
2384ca00e00Sroy 		switch (type) {
2394ca00e00Sroy 		case 'f':
2404ca00e00Sroy 			if (t->flags[i] == 1 ||
2415f630912Sroy 			    (aflag && t->flags[i] == CANCELLED_BOOLEAN))
2424ca00e00Sroy 			{
2434ca00e00Sroy 				ents[n].id = _ti_flagid(i);
2444ca00e00Sroy 				ents[n].type = 'f';
2454ca00e00Sroy 				ents[n++].flag = t->flags[i];
2464ca00e00Sroy 			}
2474ca00e00Sroy 			break;
2484ca00e00Sroy 		case 'n':
2494ca00e00Sroy 			if (VALID_NUMERIC(t->nums[i]) ||
2505f630912Sroy 			    (aflag && t->nums[i] == CANCELLED_NUMERIC))
2514ca00e00Sroy 			{
2524ca00e00Sroy 				ents[n].id = _ti_numid(i);
2534ca00e00Sroy 				ents[n].type = 'n';
2544ca00e00Sroy 				ents[n++].num = t->nums[i];
2554ca00e00Sroy 			}
2564ca00e00Sroy 			break;
2574ca00e00Sroy 		default:
2584ca00e00Sroy 			if (VALID_STRING(t->strs[i]) ||
2595f630912Sroy 			    (aflag && t->strs[i] == CANCELLED_STRING))
2604ca00e00Sroy 			{
2614ca00e00Sroy 				ents[n].id = _ti_strid(i);
2624ca00e00Sroy 				ents[n].type = 's';
2634ca00e00Sroy 				ents[n++].str = t->strs[i];
2644ca00e00Sroy 			}
2654ca00e00Sroy 			break;
2664ca00e00Sroy 		}
2674ca00e00Sroy 	}
2684ca00e00Sroy 
2694ca00e00Sroy 	if (xflag != 0 && t->_nuserdefs != 0) {
2704ca00e00Sroy 		for (i = 0; i < t->_nuserdefs; i++) {
2714ca00e00Sroy 			ud = &t->_userdefs[i];
2724ca00e00Sroy 			if (ud->type == type) {
2734ca00e00Sroy 				switch (type) {
2744ca00e00Sroy 				case 'f':
2755f630912Sroy 					if (!aflag &&
2764ca00e00Sroy 					    !VALID_BOOLEAN(ud->flag))
2774ca00e00Sroy 						continue;
2784ca00e00Sroy 					break;
2794ca00e00Sroy 				case 'n':
2805f630912Sroy 					if (!aflag &&
2814ca00e00Sroy 					    !VALID_NUMERIC(ud->num))
2824ca00e00Sroy 						continue;
2834ca00e00Sroy 					break;
2844ca00e00Sroy 				case 's':
2855f630912Sroy 					if (!aflag &&
2864ca00e00Sroy 					    !VALID_STRING(ud->str))
2874ca00e00Sroy 						continue;
2884ca00e00Sroy 					break;
2894ca00e00Sroy 				}
2904ca00e00Sroy 				ents[n].id = ud->id;
2914ca00e00Sroy 				ents[n].type = ud->type;
2924ca00e00Sroy 				ents[n].flag = ud->flag;
2934ca00e00Sroy 				ents[n].num = ud->num;
2944ca00e00Sroy 				ents[n++].str = ud->str;
2954ca00e00Sroy 			}
2964ca00e00Sroy 		}
2974ca00e00Sroy 	}
2984ca00e00Sroy 
2994ca00e00Sroy 	qsort(ents, n, sizeof(TIENT), ent_compare);
3004ca00e00Sroy 	return n;
3014ca00e00Sroy }
3024ca00e00Sroy 
3034ca00e00Sroy static void
cprint_ent(TIENT * ent)3044ca00e00Sroy cprint_ent(TIENT *ent)
3054ca00e00Sroy {
3064ca00e00Sroy 
3074ca00e00Sroy 	if (ent == NULL) {
3084ca00e00Sroy 		if (qflag == 0)
3094ca00e00Sroy 			printf("NULL");
3104ca00e00Sroy 		else
3114ca00e00Sroy 			printf("-");
3124ca00e00Sroy 	}
3134ca00e00Sroy 
3144ca00e00Sroy 	switch (ent->type) {
3154ca00e00Sroy 	case 'f':
3164ca00e00Sroy 		if (VALID_BOOLEAN(ent->flag))
3174ca00e00Sroy 			printf(ent->flag == 1 ? "T" : "F");
3184ca00e00Sroy 		else if (qflag == 0)
3194ca00e00Sroy 			printf("F");
3204ca00e00Sroy 		else if (ent->flag == CANCELLED_BOOLEAN)
3214ca00e00Sroy 			printf("@");
3224ca00e00Sroy 		else
3234ca00e00Sroy 			printf("-");
3244ca00e00Sroy 		break;
3254ca00e00Sroy 	case 'n':
3264ca00e00Sroy 		if (VALID_NUMERIC(ent->num))
3274ca00e00Sroy 			printf("%d", ent->num);
3284ca00e00Sroy 		else if (qflag == 0)
3294ca00e00Sroy 			printf("NULL");
3304ca00e00Sroy 		else if (ent->num == CANCELLED_NUMERIC)
3314ca00e00Sroy 			printf("@");
3324ca00e00Sroy 		else
3334ca00e00Sroy 			printf("-");
3344ca00e00Sroy 		break;
3354ca00e00Sroy 	case 's':
3364ca00e00Sroy 		if (VALID_STRING(ent->str)) {
3374ca00e00Sroy 			printf("'");
3384ca00e00Sroy 			outstr(stdout, ent->str);
3394ca00e00Sroy 			printf("'");
3404ca00e00Sroy 		} else if (qflag == 0)
3414ca00e00Sroy 			printf("NULL");
3424ca00e00Sroy 		else if (ent->str == CANCELLED_STRING)
3434ca00e00Sroy 			printf("@");
3444ca00e00Sroy 		else
3454ca00e00Sroy 			printf("-");
3464ca00e00Sroy 		break;
3474ca00e00Sroy 	}
3484ca00e00Sroy }
3494ca00e00Sroy 
3504ca00e00Sroy static void
compare_ents(TIENT * ents1,size_t n1,TIENT * ents2,size_t n2)3514ca00e00Sroy compare_ents(TIENT *ents1, size_t n1, TIENT *ents2, size_t n2)
3524ca00e00Sroy {
3534ca00e00Sroy 	size_t i1, i2;
3544ca00e00Sroy 	TIENT *e1, *e2, ee;
3554ca00e00Sroy 	int c;
3564ca00e00Sroy 
3574ca00e00Sroy 	i1 = i2 = 0;
3584ca00e00Sroy 	ee.type = 'f';
3594ca00e00Sroy 	ee.flag = ABSENT_BOOLEAN;
3604ca00e00Sroy 	ee.num = ABSENT_NUMERIC;
3614ca00e00Sroy 	ee.str = ABSENT_STRING;
3624ca00e00Sroy 	while (i1 != n1 || i2 != n2) {
3634ca00e00Sroy 		if (i1 == n1)
3644ca00e00Sroy 			c = 1;
3654ca00e00Sroy 		else if (i2 == n2)
3664ca00e00Sroy 			c = -1;
3674ca00e00Sroy 		else
3684ca00e00Sroy 			c = strcmp(ents1[i1].id, ents2[i2].id);
3694ca00e00Sroy 		if (c == 0) {
3704ca00e00Sroy 			e1 = &ents1[i1++];
3714ca00e00Sroy 			e2 = &ents2[i2++];
3724ca00e00Sroy 		} else if (c < 0) {
3734ca00e00Sroy 			e1 = &ents1[i1++];
3744ca00e00Sroy 			e2 = &ee;
3754ca00e00Sroy 			ee.id = e1->id;
3764ca00e00Sroy 			ee.type = e1->type;
3774ca00e00Sroy 		} else {
3784ca00e00Sroy 			e1 = &ee;
3794ca00e00Sroy 			e2 = &ents2[i2++];
3804ca00e00Sroy 			ee.id = e2->id;
3814ca00e00Sroy 			ee.type = e2->type;
3824ca00e00Sroy 		}
3834ca00e00Sroy 		switch (e1->type) {
3844ca00e00Sroy 		case 'f':
3854ca00e00Sroy 			if (cflag != 0) {
3864ca00e00Sroy 				if (e1->flag == e2->flag)
3874ca00e00Sroy 					printf("\t%s\n", ents1[i1].id);
3884ca00e00Sroy 				continue;
3894ca00e00Sroy 			}
3904ca00e00Sroy 			if (e1->flag == e2->flag)
3914ca00e00Sroy 				continue;
3924ca00e00Sroy 			break;
3934ca00e00Sroy 		case 'n':
3944ca00e00Sroy 			if (cflag != 0) {
3954ca00e00Sroy 				if (e1->num == e2->num)
3964ca00e00Sroy 					printf("\t%s#%d\n",
3974ca00e00Sroy 					    ents1[i1].id, ents1[i1].num);
3984ca00e00Sroy 				continue;
3994ca00e00Sroy 			}
4004ca00e00Sroy 			if (e1->num == e2->num)
4014ca00e00Sroy 				continue;
4024ca00e00Sroy 			break;
4034ca00e00Sroy 		case 's':
4044ca00e00Sroy 			if (cflag != 0) {
4054ca00e00Sroy 				if (VALID_STRING(e1->str) &&
4064ca00e00Sroy 				    VALID_STRING(e2->str) &&
4074ca00e00Sroy 				    strcmp(e1->str, e2->str) == 0) {
4084ca00e00Sroy 					printf("\t%s=", ents1[i1].id);
4094ca00e00Sroy 					outstr(stdout, ents1[i1].str);
4104ca00e00Sroy 					printf("\n");
4114ca00e00Sroy 				}
4124ca00e00Sroy 				continue;
4134ca00e00Sroy 			}
4144ca00e00Sroy 			if (VALID_STRING(e1->str) &&
4154ca00e00Sroy 			    VALID_STRING(e2->str) &&
4164ca00e00Sroy 			    strcmp(e1->str, e2->str) == 0)
4174ca00e00Sroy 				continue;
4184ca00e00Sroy 			break;
4194ca00e00Sroy 		}
4204ca00e00Sroy 		printf("\t%s: ", e1->id);
4214ca00e00Sroy 		cprint_ent(e1);
4224ca00e00Sroy 		if (e1->type == 'f')
4234ca00e00Sroy 			printf(":");
4244ca00e00Sroy 		else
4254ca00e00Sroy 			printf(", ");
4264ca00e00Sroy 		cprint_ent(e2);
4274ca00e00Sroy 		printf(".\n");
4284ca00e00Sroy 	}
4294ca00e00Sroy }
4304ca00e00Sroy 
4314ca00e00Sroy static TERMINAL *
load_term(const char * name)4324ca00e00Sroy load_term(const char *name)
4334ca00e00Sroy {
4344ca00e00Sroy 	TERMINAL *t;
4354ca00e00Sroy 
4360fca9bd2Schristos 	t = ecalloc(1, sizeof(*t));
4374ca00e00Sroy 	if (name == NULL)
4384ca00e00Sroy 		name = getenv("TERM");
4394ca00e00Sroy 	if (name == NULL)
4404ca00e00Sroy 		name = "dumb";
4415176b197Sroy 	if (_ti_getterm(t, name, 1) == 1)
4424ca00e00Sroy 		return t;
4435176b197Sroy 
4445176b197Sroy 	if (_ti_database == NULL)
445c33983b9Sroy 		errx(EXIT_FAILURE,
446c33983b9Sroy 		    "no terminal definition found in internal database");
4475176b197Sroy 	else
448c33983b9Sroy 		errx(EXIT_FAILURE,
449c33983b9Sroy 		    "no terminal definition found in %s.db", _ti_database);
4504ca00e00Sroy }
4514ca00e00Sroy 
4524ca00e00Sroy static void
show_missing(TERMINAL * t1,TERMINAL * t2,char type)4534ca00e00Sroy show_missing(TERMINAL *t1, TERMINAL *t2, char type)
4544ca00e00Sroy {
4554ca00e00Sroy 	ssize_t i, max;
4564ca00e00Sroy 	const char *id;
4574ca00e00Sroy 
4584ca00e00Sroy 	switch (type) {
4594ca00e00Sroy 	case 'f':
4604ca00e00Sroy 		max = TIFLAGMAX;
4614ca00e00Sroy 		break;
4624ca00e00Sroy 	case 'n':
4634ca00e00Sroy 		max = TINUMMAX;
4644ca00e00Sroy 		break;
4654ca00e00Sroy 	default:
4664ca00e00Sroy 		max = TISTRMAX;
4674ca00e00Sroy 	}
4684ca00e00Sroy 
4694ca00e00Sroy 	for (i = 0; i <= max; i++) {
4704ca00e00Sroy 		switch (type) {
4714ca00e00Sroy 		case 'f':
4724ca00e00Sroy 			if (t1->flags[i] != ABSENT_BOOLEAN ||
4734ca00e00Sroy 			    t2->flags[i] != ABSENT_BOOLEAN)
4744ca00e00Sroy 				continue;
4754ca00e00Sroy 			id = _ti_flagid(i);
4764ca00e00Sroy 			break;
4774ca00e00Sroy 		case 'n':
4784ca00e00Sroy 			if (t1->nums[i] != ABSENT_NUMERIC ||
4794ca00e00Sroy 			    t2->nums[i] != ABSENT_NUMERIC)
4804ca00e00Sroy 				continue;
4814ca00e00Sroy 			id = _ti_numid(i);
4824ca00e00Sroy 			break;
4834ca00e00Sroy 		default:
4844ca00e00Sroy 			if (t1->strs[i] != ABSENT_STRING ||
4854ca00e00Sroy 			    t2->strs[i] != ABSENT_STRING)
4864ca00e00Sroy 				continue;
4874ca00e00Sroy 			id = _ti_strid(i);
4884ca00e00Sroy 			break;
4894ca00e00Sroy 		}
4904ca00e00Sroy 		printf("\t!%s.\n", id);
4914ca00e00Sroy 	}
4924ca00e00Sroy }
4934ca00e00Sroy 
4944ca00e00Sroy static TERMUSERDEF *
find_userdef(TERMINAL * term,const char * id)4954ca00e00Sroy find_userdef(TERMINAL *term, const char *id)
4964ca00e00Sroy {
4974ca00e00Sroy 	size_t i;
4984ca00e00Sroy 
4994ca00e00Sroy 	for (i = 0; i < term->_nuserdefs; i++)
5004ca00e00Sroy 		if (strcmp(term->_userdefs[i].id, id) == 0)
5014ca00e00Sroy 			return &term->_userdefs[i];
5024ca00e00Sroy 	return NULL;
5034ca00e00Sroy }
5044ca00e00Sroy 
5054ca00e00Sroy static void
use_terms(TERMINAL * term,size_t nuse,char ** uterms)5064ca00e00Sroy use_terms(TERMINAL *term, size_t nuse, char **uterms)
5074ca00e00Sroy {
5084ca00e00Sroy 	TERMINAL **terms;
5094ca00e00Sroy 	TERMUSERDEF *ud, *tud;
5104ca00e00Sroy 	size_t i, j, agree, absent, data;
5114ca00e00Sroy 
51243ed6275Schristos 	terms = ecalloc(nuse, sizeof(*terms));
5134ca00e00Sroy 	for (i = 0; i < nuse; i++) {
5144ca00e00Sroy 		if (strcmp(term->name, *uterms) == 0)
5150fca9bd2Schristos 			errx(EXIT_FAILURE, "cannot use same terminal");
5164ca00e00Sroy 		for (j = 0; j < i; j++)
5174ca00e00Sroy 			if (strcmp(terms[j]->name, *uterms) == 0)
5180fca9bd2Schristos 				errx(EXIT_FAILURE, "cannot use same terminal");
5194ca00e00Sroy 		terms[i] = load_term(*uterms++);
5204ca00e00Sroy 	}
5214ca00e00Sroy 
5224ca00e00Sroy 	for (i = 0; i < TIFLAGMAX + 1; i++) {
5234ca00e00Sroy 		agree = absent = data = 0;
5244ca00e00Sroy 		for (j = 0; j < nuse; j++) {
5254ca00e00Sroy 			if (terms[j]->flags[i] == ABSENT_BOOLEAN ||
5264ca00e00Sroy 			    terms[j]->flags[i] == CANCELLED_BOOLEAN)
5274ca00e00Sroy 				absent++;
5284ca00e00Sroy 			else {
5294ca00e00Sroy 				data++;
5304ca00e00Sroy 				if (term->flags[i] == terms[j]->flags[i])
5314ca00e00Sroy 					agree++;
5324ca00e00Sroy 			}
5334ca00e00Sroy 		}
5344ca00e00Sroy 		if (data == 0)
5354ca00e00Sroy 			continue;
5364ca00e00Sroy 		if (agree > 0 && agree + absent == nuse)
5374ca00e00Sroy 			term->flags[i] = ABSENT_BOOLEAN;
5384ca00e00Sroy 		else if (term->flags[i] == ABSENT_BOOLEAN)
5394ca00e00Sroy 			term->flags[i] = CANCELLED_BOOLEAN;
5404ca00e00Sroy 	}
5414ca00e00Sroy 
5424ca00e00Sroy 	for (i = 0; i < TINUMMAX + 1; i++) {
5434ca00e00Sroy 		agree = absent = data = 0;
5444ca00e00Sroy 		for (j = 0; j < nuse; j++) {
5454ca00e00Sroy 			if (terms[j]->nums[i] == ABSENT_NUMERIC ||
5464ca00e00Sroy 			    terms[j]->nums[i] == CANCELLED_NUMERIC)
5474ca00e00Sroy 				absent++;
5484ca00e00Sroy 			else {
5494ca00e00Sroy 				data++;
5504ca00e00Sroy 				if (term->nums[i] == terms[j]->nums[i])
5514ca00e00Sroy 					agree++;
5524ca00e00Sroy 			}
5534ca00e00Sroy 		}
5544ca00e00Sroy 		if (data == 0)
5554ca00e00Sroy 			continue;
5564ca00e00Sroy 		if (agree > 0 && agree + absent == nuse)
5574ca00e00Sroy 			term->nums[i] = ABSENT_NUMERIC;
5584ca00e00Sroy 		else if (term->nums[i] == ABSENT_NUMERIC)
5594ca00e00Sroy 			term->nums[i] = CANCELLED_NUMERIC;
5604ca00e00Sroy 	}
5614ca00e00Sroy 
5624ca00e00Sroy 	for (i = 0; i < TISTRMAX + 1; i++) {
5634ca00e00Sroy 		agree = absent = data = 0;
5644ca00e00Sroy 		for (j = 0; j < nuse; j++) {
5654ca00e00Sroy 			if (terms[j]->strs[i] == ABSENT_STRING ||
5664ca00e00Sroy 			    terms[j]->strs[i] == CANCELLED_STRING)
5674ca00e00Sroy 				absent++;
5684ca00e00Sroy 			else {
5694ca00e00Sroy 				data++;
5704ca00e00Sroy 				if (VALID_STRING(term->strs[i]) &&
5714ca00e00Sroy 				    strcmp(term->strs[i],
5724ca00e00Sroy 					terms[j]->strs[i]) == 0)
5734ca00e00Sroy 					agree++;
5744ca00e00Sroy 			}
5754ca00e00Sroy 		}
5764ca00e00Sroy 		if (data == 0)
5774ca00e00Sroy 			continue;
5784ca00e00Sroy 		if (agree > 0 && agree + absent == nuse)
5794ca00e00Sroy 			term->strs[i] = ABSENT_STRING;
5804ca00e00Sroy 		else if (term->strs[i] == ABSENT_STRING)
5814ca00e00Sroy 			term->strs[i] = CANCELLED_STRING;
5824ca00e00Sroy 	}
5834ca00e00Sroy 
5844ca00e00Sroy 	/* User defined caps are more tricky.
5854ca00e00Sroy 	   First we set any to absent that agree. */
5864ca00e00Sroy 	for (i = 0; i < term->_nuserdefs; i++) {
5874ca00e00Sroy 		agree = absent = data = 0;
5884ca00e00Sroy 		ud = &term->_userdefs[i];
5894ca00e00Sroy 		for (j = 0; j < nuse; j++) {
5904ca00e00Sroy 			tud = find_userdef(terms[j], ud->id);
5914ca00e00Sroy 			if (tud == NULL)
5924ca00e00Sroy 				absent++;
5934ca00e00Sroy 			else {
5944ca00e00Sroy 				data++;
5954ca00e00Sroy 				switch (ud->type) {
5964ca00e00Sroy 				case 'f':
5974ca00e00Sroy 					if (tud->type == 'f' &&
5984ca00e00Sroy 					    tud->flag == ud->flag)
5994ca00e00Sroy 						agree++;
6004ca00e00Sroy 					break;
6014ca00e00Sroy 				case 'n':
6024ca00e00Sroy 					if (tud->type == 'n' &&
6034ca00e00Sroy 					    tud->num == ud->num)
6044ca00e00Sroy 						agree++;
6054ca00e00Sroy 					break;
6064ca00e00Sroy 				case 's':
6074ca00e00Sroy 					if (tud->type == 's' &&
6084ca00e00Sroy 					    VALID_STRING(tud->str) &&
6094ca00e00Sroy 					    VALID_STRING(ud->str) &&
6104ca00e00Sroy 					    strcmp(ud->str, tud->str) == 0)
6114ca00e00Sroy 						agree++;
6124ca00e00Sroy 					break;
6134ca00e00Sroy 				}
6144ca00e00Sroy 			}
6154ca00e00Sroy 		}
6164ca00e00Sroy 		if (data == 0)
6174ca00e00Sroy 			continue;
6184ca00e00Sroy 		if (agree > 0 && agree + absent == nuse) {
6194ca00e00Sroy 			ud->flag = ABSENT_BOOLEAN;
6204ca00e00Sroy 			ud->num = ABSENT_NUMERIC;
6214ca00e00Sroy 			ud->str = ABSENT_STRING;
6224ca00e00Sroy 		}
6234ca00e00Sroy 	}
6244ca00e00Sroy 
6254ca00e00Sroy 	/* Now add any that we don't have as cancelled */
6264ca00e00Sroy 	for (i = 0; i < nuse; i++) {
6274ca00e00Sroy 		for (j = 0; j < terms[i]->_nuserdefs; j++) {
6284ca00e00Sroy 			ud = find_userdef(term, terms[i]->_userdefs[j].id);
6294ca00e00Sroy 			if (ud != NULL)
6304ca00e00Sroy 				continue; /* We have handled this */
6310fca9bd2Schristos 			term->_userdefs = erealloc(term->_userdefs,
6324ca00e00Sroy 			    sizeof(*term->_userdefs) * (term->_nuserdefs + 1));
6334ca00e00Sroy 			tud = &term->_userdefs[term->_nuserdefs++];
6344ca00e00Sroy 			tud->id = terms[i]->_userdefs[j].id;
6354ca00e00Sroy 			tud->type = terms[i]->_userdefs[j].flag;
6364ca00e00Sroy 			tud->flag = CANCELLED_BOOLEAN;
6374ca00e00Sroy 			tud->num = CANCELLED_NUMERIC;
6384ca00e00Sroy 			tud->str = CANCELLED_STRING;
6394ca00e00Sroy 		}
6404ca00e00Sroy 	}
6414ca00e00Sroy }
6424ca00e00Sroy 
6434ca00e00Sroy int
main(int argc,char ** argv)6444ca00e00Sroy main(int argc, char **argv)
6454ca00e00Sroy {
6464ca00e00Sroy 	char *term, *Barg;
6474ca00e00Sroy 	int ch, uflag;
6484ca00e00Sroy 	TERMINAL *t, *t2;
6494ca00e00Sroy 	size_t n, n2;
6504ca00e00Sroy 	struct winsize ws;
6514ca00e00Sroy 	TIENT ents[TISTRMAX + 1], ents2[TISTRMAX + 1];
6524ca00e00Sroy 
6534ca00e00Sroy 	cols = 80; /* default */
6544ca00e00Sroy 	term = getenv("COLUMNS");
6554ca00e00Sroy 	if (term != NULL)
6564ca00e00Sroy 		cols = strtoul(term, NULL, 10);
6574ca00e00Sroy 	else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
6584ca00e00Sroy 		cols = ws.ws_col;
6594ca00e00Sroy 
6604ca00e00Sroy 	uflag = xflag = 0;
6614ca00e00Sroy 	Barg = NULL;
6624ca00e00Sroy 	while ((ch = getopt(argc, argv, "1A:B:acnquw:x")) != -1)
6634ca00e00Sroy 		switch (ch) {
6644ca00e00Sroy 		case '1':
6654ca00e00Sroy 			cols = 1;
6664ca00e00Sroy 			break;
6674ca00e00Sroy 		case 'A':
6684ca00e00Sroy 			setdb(optarg);
6694ca00e00Sroy 			break;
6704ca00e00Sroy 		case 'B':
6714ca00e00Sroy 			Barg = optarg;
6724ca00e00Sroy 			break;
6734ca00e00Sroy 		case 'a':
6745f630912Sroy 			aflag = 1;
6754ca00e00Sroy 			break;
6764ca00e00Sroy 		case 'c':
6775f630912Sroy 			cflag = 1;
6784ca00e00Sroy 			break;
6794ca00e00Sroy 		case 'n':
6805f630912Sroy 			nflag = 1;
6814ca00e00Sroy 			break;
6824ca00e00Sroy 		case 'q':
6835f630912Sroy 			qflag = 1;
6844ca00e00Sroy 			break;
6854ca00e00Sroy 		case 'u':
6865f630912Sroy 			uflag = 1;
6875f630912Sroy 			aflag = 1;
6884ca00e00Sroy 			break;
6894ca00e00Sroy 		case 'w':
6904ca00e00Sroy 			cols = strtoul(optarg, NULL, 10);
6914ca00e00Sroy 			break;
6924ca00e00Sroy 		case 'x':
6935f630912Sroy 			xflag = 1;
6944ca00e00Sroy 			break;
6954ca00e00Sroy 		case '?':
6964ca00e00Sroy 		default:
6974ca00e00Sroy 			fprintf(stderr,
6984ca00e00Sroy 			    "usage: %s [-1acnqux] [-A database] [-B database] "
6994ca00e00Sroy 			    "[-w cols] [term]\n",
7004ca00e00Sroy 			    getprogname());
7014ca00e00Sroy 			return EXIT_FAILURE;
7024ca00e00Sroy 		}
7034ca00e00Sroy 	cols--;
7044ca00e00Sroy 
7054ca00e00Sroy 	if (optind + 1 < argc)
7065f630912Sroy 		aflag = 1;
7074ca00e00Sroy 
7084ca00e00Sroy 	if (optind < argc)
7094ca00e00Sroy 		term = argv[optind++];
7104ca00e00Sroy 	else
7114ca00e00Sroy 		term = NULL;
7124ca00e00Sroy 	t = load_term(term);
7134ca00e00Sroy 
7144ca00e00Sroy 	if (uflag != 0)
7154ca00e00Sroy 		use_terms(t, argc - optind, argv + optind);
7164ca00e00Sroy 
7174ca00e00Sroy 	if ((optind + 1 != argc && nflag == 0) || uflag != 0) {
718f5e0f030Sroy 		if (uflag == 0)
719f5e0f030Sroy 			printf("# Reconstructed from %s\n",
720f5e0f030Sroy 			     _ti_database == NULL ?
721f5e0f030Sroy 			     "internal database" : _ti_database);
722a46bbbf6Sroy 		/* Strip internal versioning */
723a46bbbf6Sroy 		term = strchr(t->name, TERMINFO_VDELIM);
724a46bbbf6Sroy 		if (term != NULL)
725a46bbbf6Sroy 			*term = '\0';
7264ca00e00Sroy 		printf("%s", t->name);
727*fe2a18efSroy 		if (t->_alias != NULL) {
728*fe2a18efSroy 			char *alias, *aliascpy, *delim;
729*fe2a18efSroy 
730cd83b487Sroy 			alias = aliascpy = estrdup(t->_alias);
731cd83b487Sroy 			while (alias != NULL && *alias != '\0') {
732cd83b487Sroy 				putchar('|');
733cd83b487Sroy 				delim = strchr(alias, TERMINFO_VDELIM);
734cd83b487Sroy 				if (delim != NULL)
735cd83b487Sroy 					*delim++ = '\0';
736cd83b487Sroy 				printf("%s", alias);
737cd83b487Sroy 				if (delim != NULL) {
738cd83b487Sroy 					while (*delim != '\0' && *delim != '|')
739cd83b487Sroy 						delim++;
740cd83b487Sroy 					if (*delim == '\0')
741cd83b487Sroy 						alias = NULL;
742cd83b487Sroy 					else
743cd83b487Sroy 						alias = delim + 1;
744cd83b487Sroy 				} else
745cd83b487Sroy 					alias = NULL;
746cd83b487Sroy 			}
747cd83b487Sroy 			free(aliascpy);
748*fe2a18efSroy 		}
7494ca00e00Sroy 		if (t->desc != NULL && *t->desc != '\0')
7504ca00e00Sroy 			printf("|%s", t->desc);
7514ca00e00Sroy 		printf(",\n");
7524ca00e00Sroy 
7534ca00e00Sroy 		n = load_ents(ents, t, 'f');
7544ca00e00Sroy 		print_ent(ents, n);
7554ca00e00Sroy 		n = load_ents(ents, t, 'n');
7564ca00e00Sroy 		print_ent(ents, n);
7574ca00e00Sroy 		n = load_ents(ents, t, 's');
7584ca00e00Sroy 		print_ent(ents, n);
7594ca00e00Sroy 
7604ca00e00Sroy 		if (uflag != 0) {
7614ca00e00Sroy 			printf("\t");
7624ca00e00Sroy 			n = SW;
7634ca00e00Sroy 			for (; optind < argc; optind++) {
7644ca00e00Sroy 				n2 = 5 + strlen(argv[optind]);
7654ca00e00Sroy 				if (n != SW) {
7664ca00e00Sroy 					if (n + n2 > cols) {
7674ca00e00Sroy 						printf("\n\t");
7684ca00e00Sroy 						n = SW;
7694ca00e00Sroy 					} else
7704ca00e00Sroy 						n += printf(" ");
7714ca00e00Sroy 				}
7724ca00e00Sroy 				n += printf("use=%s,", argv[optind]);
7734ca00e00Sroy 			}
7744ca00e00Sroy 			printf("\n");
7754ca00e00Sroy 		}
7764ca00e00Sroy 		return EXIT_SUCCESS;
7774ca00e00Sroy 	}
7784ca00e00Sroy 
7794ca00e00Sroy 	if (Barg == NULL)
7804ca00e00Sroy 		unsetenv("TERMINFO");
7814ca00e00Sroy 	else
7824ca00e00Sroy 		setdb(Barg);
7834ca00e00Sroy 	t2 = load_term(argv[optind++]);
7844ca00e00Sroy 	printf("comparing %s to %s.\n", t->name, t2->name);
7854ca00e00Sroy 	if (qflag == 0)
7864ca00e00Sroy 		printf("    comparing booleans.\n");
7874ca00e00Sroy 	if (nflag == 0) {
7884ca00e00Sroy 		n = load_ents(ents, t, 'f');
7894ca00e00Sroy 		n2 = load_ents(ents2, t2, 'f');
7904ca00e00Sroy 		compare_ents(ents, n, ents2, n2);
7914ca00e00Sroy 	} else
7924ca00e00Sroy 		show_missing(t, t2, 'f');
7934ca00e00Sroy 	if (qflag == 0)
7944ca00e00Sroy 		printf("    comparing numbers.\n");
7954ca00e00Sroy 	if (nflag == 0) {
7964ca00e00Sroy 		n = load_ents(ents, t, 'n');
7974ca00e00Sroy 		n2 = load_ents(ents2, t2, 'n');
7984ca00e00Sroy 		compare_ents(ents, n, ents2, n2);
7994ca00e00Sroy 	} else
8004ca00e00Sroy 		show_missing(t, t2, 'n');
8014ca00e00Sroy 	if (qflag == 0)
8024ca00e00Sroy 		printf("    comparing strings.\n");
8034ca00e00Sroy 	if (nflag == 0) {
8044ca00e00Sroy 		n = load_ents(ents, t, 's');
8054ca00e00Sroy 		n2 = load_ents(ents2, t2, 's');
8064ca00e00Sroy 		compare_ents(ents, n, ents2, n2);
8074ca00e00Sroy 	} else
8084ca00e00Sroy 		show_missing(t, t2, 's');
8094ca00e00Sroy 	return EXIT_SUCCESS;
8104ca00e00Sroy }
811