Home | History | Annotate | Line # | Download | only in infocmp
      1 /* $NetBSD: infocmp.c,v 1.17 2020/03/31 12:44:15 roy Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2009, 2010, 2020 The NetBSD Foundation, Inc.
      5  *
      6  * This code is derived from software contributed to The NetBSD Foundation
      7  * by Roy Marples.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __RCSID("$NetBSD: infocmp.c,v 1.17 2020/03/31 12:44:15 roy Exp $");
     32 
     33 #include <sys/ioctl.h>
     34 
     35 #include <ctype.h>
     36 #include <err.h>
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <term_private.h>
     41 #include <term.h>
     42 #include <unistd.h>
     43 #include <util.h>
     44 
     45 #define SW 8
     46 
     47 typedef struct tient {
     48 	char type;
     49 	const char *id;
     50 	signed char flag;
     51 	int num;
     52 	const char *str;
     53 } TIENT;
     54 
     55 static size_t cols;
     56 static int aflag, cflag, nflag, qflag, xflag;
     57 
     58 static size_t
     59 outstr(FILE *f, const char *str)
     60 {
     61 	unsigned char ch;
     62 	size_t r, l;
     63 
     64 	r = 0;
     65 	l = strlen(str);
     66 	while ((ch = (unsigned char)(*str++)) != '\0') {
     67 		switch (ch) {
     68 		case 128:
     69 			ch = '0';
     70 			break;
     71 		case '\033':
     72 			ch = 'E';
     73 			break;
     74 		case '\014':
     75 			ch = 'f';
     76 			break;
     77 		case '^': /* FALLTHROUGH */
     78 		case ',': /* escape these */
     79 			break;
     80 		case ' ':
     81 			ch = 's';
     82 			break;
     83 		default:
     84 			if (ch == '\177') {
     85 				if (f != NULL)
     86 					fputc('^', f);
     87 				ch = '?';
     88 				r++;
     89 			} else if (iscntrl(ch) &&
     90 			    ch < 128 &&
     91 			    ch != '\\' &&
     92 			    (l < 4 || isdigit((unsigned char)*str)))
     93 			{
     94 				if (f != NULL)
     95 					fputc('^', f);
     96 				ch += '@';
     97 				r++;
     98 			} else if (!isprint(ch)) {
     99 				if (f != NULL)
    100 					fprintf(f, "\\%03o", ch);
    101 				r += 4;
    102 				continue;
    103 			}
    104 			goto prnt;
    105 		}
    106 
    107 		if (f != NULL)
    108 			fputc('\\', f);
    109 		r++;
    110 prnt:
    111 		if (f != NULL)
    112 			fputc(ch, f);
    113 		r++;
    114 	}
    115 	return r;
    116 }
    117 
    118 static int
    119 ent_compare(const void *a, const void *b)
    120 {
    121 	const TIENT *ta, *tb;
    122 
    123 	ta = (const TIENT *)a;
    124 	tb = (const TIENT *)b;
    125 	return strcmp(ta->id, tb->id);
    126 }
    127 
    128 static void
    129 setdb(char *db)
    130 {
    131 	static const char *ext[] = { ".cdb", ".db" };
    132 
    133 	for (size_t i = 0; i < __arraycount(ext); i++) {
    134 		char *ptr = strstr(db, ext[i]);
    135 		if (ptr == NULL || ptr[strlen(ext[i])] != '\0')
    136 			continue;
    137 		*ptr = '\0';
    138 		break;
    139 	}
    140 	setenv("TERMINFO", db, 1);
    141 }
    142 
    143 static void
    144 print_ent(const TIENT *ents, size_t nents)
    145 {
    146 	size_t col, i, l;
    147 	char nbuf[64];
    148 
    149 	if (nents == 0)
    150 		return;
    151 
    152 	col = SW;
    153 	printf("\t");
    154 	for (i = 0; i < nents; i++) {
    155 		if (*ents[i].id == '.' && aflag == 0)
    156 			continue;
    157 		switch (ents[i].type) {
    158 		case 'f':
    159 			if (ents[i].flag == ABSENT_BOOLEAN)
    160 				continue;
    161 			l = strlen(ents[i].id) + 2;
    162 			if (ents[i].flag == CANCELLED_BOOLEAN)
    163 				l++;
    164 			break;
    165 		case 'n':
    166 			if (ents[i].num == ABSENT_NUMERIC)
    167 				continue;
    168 			if (VALID_NUMERIC(ents[i].num))
    169 				l = snprintf(nbuf, sizeof(nbuf), "%s#%d,",
    170 				    ents[i].id, ents[i].num);
    171 			else
    172 				l = snprintf(nbuf, sizeof(nbuf), "%s@,",
    173 				    ents[i].id);
    174 			break;
    175 		case 's':
    176 			if (ents[i].str == ABSENT_STRING)
    177 				continue;
    178 			if (VALID_STRING(ents[i].str))
    179 				l = strlen(ents[i].id) +
    180 				    outstr(NULL, ents[i].str) + 7;
    181 			else
    182 				l = strlen(ents[i].id) + 3;
    183 			break;
    184 		default:
    185 			errx(EXIT_FAILURE, "invalid type");
    186 		}
    187 		if (col != SW) {
    188 			if (col + l > cols) {
    189 				printf("\n\t");
    190 				col = SW;
    191 			} else
    192 				col += printf(" ");
    193 		}
    194 		switch (ents[i].type) {
    195 		case 'f':
    196 			col += printf("%s", ents[i].id);
    197 			if (ents[i].flag == ABSENT_BOOLEAN ||
    198 			    ents[i].flag == CANCELLED_BOOLEAN)
    199 				col += printf("@");
    200 			col += printf(",");
    201 			break;
    202 		case 'n':
    203 			col += printf("%s", nbuf);
    204 			break;
    205 		case 's':
    206 			col += printf("%s", ents[i].id);
    207 			if (VALID_STRING(ents[i].str)) {
    208 				col += printf("=");
    209 				col += outstr(stdout, ents[i].str);
    210 			} else
    211 				col += printf("@");
    212 			col += printf(",");
    213 			break;
    214 		}
    215 	}
    216 	printf("\n");
    217 }
    218 
    219 static size_t
    220 load_ents(TIENT *ents, TERMINAL *t, char type)
    221 {
    222 	size_t i, n, max;
    223 	TERMUSERDEF *ud;
    224 
    225 	switch (type) {
    226 	case 'f':
    227 		max = TIFLAGMAX;
    228 		break;
    229 	case 'n':
    230 		max = TINUMMAX;
    231 		break;
    232 	default:
    233 		max = TISTRMAX;
    234 	}
    235 
    236 	n = 0;
    237 	for (i = 0; i <= max; i++) {
    238 		switch (type) {
    239 		case 'f':
    240 			if (t->flags[i] == 1 ||
    241 			    (aflag && t->flags[i] == CANCELLED_BOOLEAN))
    242 			{
    243 				ents[n].id = _ti_flagid(i);
    244 				ents[n].type = 'f';
    245 				ents[n++].flag = t->flags[i];
    246 			}
    247 			break;
    248 		case 'n':
    249 			if (VALID_NUMERIC(t->nums[i]) ||
    250 			    (aflag && t->nums[i] == CANCELLED_NUMERIC))
    251 			{
    252 				ents[n].id = _ti_numid(i);
    253 				ents[n].type = 'n';
    254 				ents[n++].num = t->nums[i];
    255 			}
    256 			break;
    257 		default:
    258 			if (VALID_STRING(t->strs[i]) ||
    259 			    (aflag && t->strs[i] == CANCELLED_STRING))
    260 			{
    261 				ents[n].id = _ti_strid(i);
    262 				ents[n].type = 's';
    263 				ents[n++].str = t->strs[i];
    264 			}
    265 			break;
    266 		}
    267 	}
    268 
    269 	if (xflag != 0 && t->_nuserdefs != 0) {
    270 		for (i = 0; i < t->_nuserdefs; i++) {
    271 			ud = &t->_userdefs[i];
    272 			if (ud->type == type) {
    273 				switch (type) {
    274 				case 'f':
    275 					if (!aflag &&
    276 					    !VALID_BOOLEAN(ud->flag))
    277 						continue;
    278 					break;
    279 				case 'n':
    280 					if (!aflag &&
    281 					    !VALID_NUMERIC(ud->num))
    282 						continue;
    283 					break;
    284 				case 's':
    285 					if (!aflag &&
    286 					    !VALID_STRING(ud->str))
    287 						continue;
    288 					break;
    289 				}
    290 				ents[n].id = ud->id;
    291 				ents[n].type = ud->type;
    292 				ents[n].flag = ud->flag;
    293 				ents[n].num = ud->num;
    294 				ents[n++].str = ud->str;
    295 			}
    296 		}
    297 	}
    298 
    299 	qsort(ents, n, sizeof(TIENT), ent_compare);
    300 	return n;
    301 }
    302 
    303 static void
    304 cprint_ent(TIENT *ent)
    305 {
    306 
    307 	if (ent == NULL) {
    308 		if (qflag == 0)
    309 			printf("NULL");
    310 		else
    311 			printf("-");
    312 	}
    313 
    314 	switch (ent->type) {
    315 	case 'f':
    316 		if (VALID_BOOLEAN(ent->flag))
    317 			printf(ent->flag == 1 ? "T" : "F");
    318 		else if (qflag == 0)
    319 			printf("F");
    320 		else if (ent->flag == CANCELLED_BOOLEAN)
    321 			printf("@");
    322 		else
    323 			printf("-");
    324 		break;
    325 	case 'n':
    326 		if (VALID_NUMERIC(ent->num))
    327 			printf("%d", ent->num);
    328 		else if (qflag == 0)
    329 			printf("NULL");
    330 		else if (ent->num == CANCELLED_NUMERIC)
    331 			printf("@");
    332 		else
    333 			printf("-");
    334 		break;
    335 	case 's':
    336 		if (VALID_STRING(ent->str)) {
    337 			printf("'");
    338 			outstr(stdout, ent->str);
    339 			printf("'");
    340 		} else if (qflag == 0)
    341 			printf("NULL");
    342 		else if (ent->str == CANCELLED_STRING)
    343 			printf("@");
    344 		else
    345 			printf("-");
    346 		break;
    347 	}
    348 }
    349 
    350 static void
    351 compare_ents(TIENT *ents1, size_t n1, TIENT *ents2, size_t n2)
    352 {
    353 	size_t i1, i2;
    354 	TIENT *e1, *e2, ee;
    355 	int c;
    356 
    357 	i1 = i2 = 0;
    358 	ee.type = 'f';
    359 	ee.flag = ABSENT_BOOLEAN;
    360 	ee.num = ABSENT_NUMERIC;
    361 	ee.str = ABSENT_STRING;
    362 	while (i1 != n1 || i2 != n2) {
    363 		if (i1 == n1)
    364 			c = 1;
    365 		else if (i2 == n2)
    366 			c = -1;
    367 		else
    368 			c = strcmp(ents1[i1].id, ents2[i2].id);
    369 		if (c == 0) {
    370 			e1 = &ents1[i1++];
    371 			e2 = &ents2[i2++];
    372 		} else if (c < 0) {
    373 			e1 = &ents1[i1++];
    374 			e2 = &ee;
    375 			ee.id = e1->id;
    376 			ee.type = e1->type;
    377 		} else {
    378 			e1 = &ee;
    379 			e2 = &ents2[i2++];
    380 			ee.id = e2->id;
    381 			ee.type = e2->type;
    382 		}
    383 		switch (e1->type) {
    384 		case 'f':
    385 			if (cflag != 0) {
    386 				if (e1->flag == e2->flag)
    387 					printf("\t%s\n", ents1[i1].id);
    388 				continue;
    389 			}
    390 			if (e1->flag == e2->flag)
    391 				continue;
    392 			break;
    393 		case 'n':
    394 			if (cflag != 0) {
    395 				if (e1->num == e2->num)
    396 					printf("\t%s#%d\n",
    397 					    ents1[i1].id, ents1[i1].num);
    398 				continue;
    399 			}
    400 			if (e1->num == e2->num)
    401 				continue;
    402 			break;
    403 		case 's':
    404 			if (cflag != 0) {
    405 				if (VALID_STRING(e1->str) &&
    406 				    VALID_STRING(e2->str) &&
    407 				    strcmp(e1->str, e2->str) == 0) {
    408 					printf("\t%s=", ents1[i1].id);
    409 					outstr(stdout, ents1[i1].str);
    410 					printf("\n");
    411 				}
    412 				continue;
    413 			}
    414 			if (VALID_STRING(e1->str) &&
    415 			    VALID_STRING(e2->str) &&
    416 			    strcmp(e1->str, e2->str) == 0)
    417 				continue;
    418 			break;
    419 		}
    420 		printf("\t%s: ", e1->id);
    421 		cprint_ent(e1);
    422 		if (e1->type == 'f')
    423 			printf(":");
    424 		else
    425 			printf(", ");
    426 		cprint_ent(e2);
    427 		printf(".\n");
    428 	}
    429 }
    430 
    431 static TERMINAL *
    432 load_term(const char *name)
    433 {
    434 	TERMINAL *t;
    435 
    436 	t = ecalloc(1, sizeof(*t));
    437 	if (name == NULL)
    438 		name = getenv("TERM");
    439 	if (name == NULL)
    440 		name = "dumb";
    441 	if (_ti_getterm(t, name, 1) == 1)
    442 		return t;
    443 
    444 	if (_ti_database == NULL)
    445 		errx(EXIT_FAILURE,
    446 		    "no terminal definition found in internal database");
    447 	else
    448 		errx(EXIT_FAILURE,
    449 		    "no terminal definition found in %s.db", _ti_database);
    450 }
    451 
    452 static void
    453 show_missing(TERMINAL *t1, TERMINAL *t2, char type)
    454 {
    455 	ssize_t i, max;
    456 	const char *id;
    457 
    458 	switch (type) {
    459 	case 'f':
    460 		max = TIFLAGMAX;
    461 		break;
    462 	case 'n':
    463 		max = TINUMMAX;
    464 		break;
    465 	default:
    466 		max = TISTRMAX;
    467 	}
    468 
    469 	for (i = 0; i <= max; i++) {
    470 		switch (type) {
    471 		case 'f':
    472 			if (t1->flags[i] != ABSENT_BOOLEAN ||
    473 			    t2->flags[i] != ABSENT_BOOLEAN)
    474 				continue;
    475 			id = _ti_flagid(i);
    476 			break;
    477 		case 'n':
    478 			if (t1->nums[i] != ABSENT_NUMERIC ||
    479 			    t2->nums[i] != ABSENT_NUMERIC)
    480 				continue;
    481 			id = _ti_numid(i);
    482 			break;
    483 		default:
    484 			if (t1->strs[i] != ABSENT_STRING ||
    485 			    t2->strs[i] != ABSENT_STRING)
    486 				continue;
    487 			id = _ti_strid(i);
    488 			break;
    489 		}
    490 		printf("\t!%s.\n", id);
    491 	}
    492 }
    493 
    494 static TERMUSERDEF *
    495 find_userdef(TERMINAL *term, const char *id)
    496 {
    497 	size_t i;
    498 
    499 	for (i = 0; i < term->_nuserdefs; i++)
    500 		if (strcmp(term->_userdefs[i].id, id) == 0)
    501 			return &term->_userdefs[i];
    502 	return NULL;
    503 }
    504 
    505 static void
    506 use_terms(TERMINAL *term, size_t nuse, char **uterms)
    507 {
    508 	TERMINAL **terms;
    509 	TERMUSERDEF *ud, *tud;
    510 	size_t i, j, agree, absent, data;
    511 
    512 	terms = ecalloc(nuse, sizeof(*terms));
    513 	for (i = 0; i < nuse; i++) {
    514 		if (strcmp(term->name, *uterms) == 0)
    515 			errx(EXIT_FAILURE, "cannot use same terminal");
    516 		for (j = 0; j < i; j++)
    517 			if (strcmp(terms[j]->name, *uterms) == 0)
    518 				errx(EXIT_FAILURE, "cannot use same terminal");
    519 		terms[i] = load_term(*uterms++);
    520 	}
    521 
    522 	for (i = 0; i < TIFLAGMAX + 1; i++) {
    523 		agree = absent = data = 0;
    524 		for (j = 0; j < nuse; j++) {
    525 			if (terms[j]->flags[i] == ABSENT_BOOLEAN ||
    526 			    terms[j]->flags[i] == CANCELLED_BOOLEAN)
    527 				absent++;
    528 			else {
    529 				data++;
    530 				if (term->flags[i] == terms[j]->flags[i])
    531 					agree++;
    532 			}
    533 		}
    534 		if (data == 0)
    535 			continue;
    536 		if (agree > 0 && agree + absent == nuse)
    537 			term->flags[i] = ABSENT_BOOLEAN;
    538 		else if (term->flags[i] == ABSENT_BOOLEAN)
    539 			term->flags[i] = CANCELLED_BOOLEAN;
    540 	}
    541 
    542 	for (i = 0; i < TINUMMAX + 1; i++) {
    543 		agree = absent = data = 0;
    544 		for (j = 0; j < nuse; j++) {
    545 			if (terms[j]->nums[i] == ABSENT_NUMERIC ||
    546 			    terms[j]->nums[i] == CANCELLED_NUMERIC)
    547 				absent++;
    548 			else {
    549 				data++;
    550 				if (term->nums[i] == terms[j]->nums[i])
    551 					agree++;
    552 			}
    553 		}
    554 		if (data == 0)
    555 			continue;
    556 		if (agree > 0 && agree + absent == nuse)
    557 			term->nums[i] = ABSENT_NUMERIC;
    558 		else if (term->nums[i] == ABSENT_NUMERIC)
    559 			term->nums[i] = CANCELLED_NUMERIC;
    560 	}
    561 
    562 	for (i = 0; i < TISTRMAX + 1; i++) {
    563 		agree = absent = data = 0;
    564 		for (j = 0; j < nuse; j++) {
    565 			if (terms[j]->strs[i] == ABSENT_STRING ||
    566 			    terms[j]->strs[i] == CANCELLED_STRING)
    567 				absent++;
    568 			else {
    569 				data++;
    570 				if (VALID_STRING(term->strs[i]) &&
    571 				    strcmp(term->strs[i],
    572 					terms[j]->strs[i]) == 0)
    573 					agree++;
    574 			}
    575 		}
    576 		if (data == 0)
    577 			continue;
    578 		if (agree > 0 && agree + absent == nuse)
    579 			term->strs[i] = ABSENT_STRING;
    580 		else if (term->strs[i] == ABSENT_STRING)
    581 			term->strs[i] = CANCELLED_STRING;
    582 	}
    583 
    584 	/* User defined caps are more tricky.
    585 	   First we set any to absent that agree. */
    586 	for (i = 0; i < term->_nuserdefs; i++) {
    587 		agree = absent = data = 0;
    588 		ud = &term->_userdefs[i];
    589 		for (j = 0; j < nuse; j++) {
    590 			tud = find_userdef(terms[j], ud->id);
    591 			if (tud == NULL)
    592 				absent++;
    593 			else {
    594 				data++;
    595 				switch (ud->type) {
    596 				case 'f':
    597 					if (tud->type == 'f' &&
    598 					    tud->flag == ud->flag)
    599 						agree++;
    600 					break;
    601 				case 'n':
    602 					if (tud->type == 'n' &&
    603 					    tud->num == ud->num)
    604 						agree++;
    605 					break;
    606 				case 's':
    607 					if (tud->type == 's' &&
    608 					    VALID_STRING(tud->str) &&
    609 					    VALID_STRING(ud->str) &&
    610 					    strcmp(ud->str, tud->str) == 0)
    611 						agree++;
    612 					break;
    613 				}
    614 			}
    615 		}
    616 		if (data == 0)
    617 			continue;
    618 		if (agree > 0 && agree + absent == nuse) {
    619 			ud->flag = ABSENT_BOOLEAN;
    620 			ud->num = ABSENT_NUMERIC;
    621 			ud->str = ABSENT_STRING;
    622 		}
    623 	}
    624 
    625 	/* Now add any that we don't have as cancelled */
    626 	for (i = 0; i < nuse; i++) {
    627 		for (j = 0; j < terms[i]->_nuserdefs; j++) {
    628 			ud = find_userdef(term, terms[i]->_userdefs[j].id);
    629 			if (ud != NULL)
    630 				continue; /* We have handled this */
    631 			term->_userdefs = erealloc(term->_userdefs,
    632 			    sizeof(*term->_userdefs) * (term->_nuserdefs + 1));
    633 			tud = &term->_userdefs[term->_nuserdefs++];
    634 			tud->id = terms[i]->_userdefs[j].id;
    635 			tud->type = terms[i]->_userdefs[j].flag;
    636 			tud->flag = CANCELLED_BOOLEAN;
    637 			tud->num = CANCELLED_NUMERIC;
    638 			tud->str = CANCELLED_STRING;
    639 		}
    640 	}
    641 }
    642 
    643 int
    644 main(int argc, char **argv)
    645 {
    646 	char *term, *Barg;
    647 	int ch, uflag;
    648 	TERMINAL *t, *t2;
    649 	size_t n, n2;
    650 	struct winsize ws;
    651 	TIENT ents[TISTRMAX + 1], ents2[TISTRMAX + 1];
    652 
    653 	cols = 80; /* default */
    654 	term = getenv("COLUMNS");
    655 	if (term != NULL)
    656 		cols = strtoul(term, NULL, 10);
    657 	else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
    658 		cols = ws.ws_col;
    659 
    660 	uflag = xflag = 0;
    661 	Barg = NULL;
    662 	while ((ch = getopt(argc, argv, "1A:B:acnquw:x")) != -1)
    663 		switch (ch) {
    664 		case '1':
    665 			cols = 1;
    666 			break;
    667 		case 'A':
    668 			setdb(optarg);
    669 			break;
    670 		case 'B':
    671 			Barg = optarg;
    672 			break;
    673 		case 'a':
    674 			aflag = 1;
    675 			break;
    676 		case 'c':
    677 			cflag = 1;
    678 			break;
    679 		case 'n':
    680 			nflag = 1;
    681 			break;
    682 		case 'q':
    683 			qflag = 1;
    684 			break;
    685 		case 'u':
    686 			uflag = 1;
    687 			aflag = 1;
    688 			break;
    689 		case 'w':
    690 			cols = strtoul(optarg, NULL, 10);
    691 			break;
    692 		case 'x':
    693 			xflag = 1;
    694 			break;
    695 		case '?':
    696 		default:
    697 			fprintf(stderr,
    698 			    "usage: %s [-1acnqux] [-A database] [-B database] "
    699 			    "[-w cols] [term]\n",
    700 			    getprogname());
    701 			return EXIT_FAILURE;
    702 		}
    703 	cols--;
    704 
    705 	if (optind + 1 < argc)
    706 		aflag = 1;
    707 
    708 	if (optind < argc)
    709 		term = argv[optind++];
    710 	else
    711 		term = NULL;
    712 	t = load_term(term);
    713 
    714 	if (uflag != 0)
    715 		use_terms(t, argc - optind, argv + optind);
    716 
    717 	if ((optind + 1 != argc && nflag == 0) || uflag != 0) {
    718 		if (uflag == 0)
    719 			printf("# Reconstructed from %s\n",
    720 			     _ti_database == NULL ?
    721 			     "internal database" : _ti_database);
    722 		/* Strip internal versioning */
    723 		term = strchr(t->name, TERMINFO_VDELIM);
    724 		if (term != NULL)
    725 			*term = '\0';
    726 		printf("%s", t->name);
    727 		if (t->_alias != NULL) {
    728 			char *alias, *aliascpy, *delim;
    729 
    730 			alias = aliascpy = estrdup(t->_alias);
    731 			while (alias != NULL && *alias != '\0') {
    732 				putchar('|');
    733 				delim = strchr(alias, TERMINFO_VDELIM);
    734 				if (delim != NULL)
    735 					*delim++ = '\0';
    736 				printf("%s", alias);
    737 				if (delim != NULL) {
    738 					while (*delim != '\0' && *delim != '|')
    739 						delim++;
    740 					if (*delim == '\0')
    741 						alias = NULL;
    742 					else
    743 						alias = delim + 1;
    744 				} else
    745 					alias = NULL;
    746 			}
    747 			free(aliascpy);
    748 		}
    749 		if (t->desc != NULL && *t->desc != '\0')
    750 			printf("|%s", t->desc);
    751 		printf(",\n");
    752 
    753 		n = load_ents(ents, t, 'f');
    754 		print_ent(ents, n);
    755 		n = load_ents(ents, t, 'n');
    756 		print_ent(ents, n);
    757 		n = load_ents(ents, t, 's');
    758 		print_ent(ents, n);
    759 
    760 		if (uflag != 0) {
    761 			printf("\t");
    762 			n = SW;
    763 			for (; optind < argc; optind++) {
    764 				n2 = 5 + strlen(argv[optind]);
    765 				if (n != SW) {
    766 					if (n + n2 > cols) {
    767 						printf("\n\t");
    768 						n = SW;
    769 					} else
    770 						n += printf(" ");
    771 				}
    772 				n += printf("use=%s,", argv[optind]);
    773 			}
    774 			printf("\n");
    775 		}
    776 		return EXIT_SUCCESS;
    777 	}
    778 
    779 	if (Barg == NULL)
    780 		unsetenv("TERMINFO");
    781 	else
    782 		setdb(Barg);
    783 	t2 = load_term(argv[optind++]);
    784 	printf("comparing %s to %s.\n", t->name, t2->name);
    785 	if (qflag == 0)
    786 		printf("    comparing booleans.\n");
    787 	if (nflag == 0) {
    788 		n = load_ents(ents, t, 'f');
    789 		n2 = load_ents(ents2, t2, 'f');
    790 		compare_ents(ents, n, ents2, n2);
    791 	} else
    792 		show_missing(t, t2, 'f');
    793 	if (qflag == 0)
    794 		printf("    comparing numbers.\n");
    795 	if (nflag == 0) {
    796 		n = load_ents(ents, t, 'n');
    797 		n2 = load_ents(ents2, t2, 'n');
    798 		compare_ents(ents, n, ents2, n2);
    799 	} else
    800 		show_missing(t, t2, 'n');
    801 	if (qflag == 0)
    802 		printf("    comparing strings.\n");
    803 	if (nflag == 0) {
    804 		n = load_ents(ents, t, 's');
    805 		n2 = load_ents(ents2, t2, 's');
    806 		compare_ents(ents, n, ents2, n2);
    807 	} else
    808 		show_missing(t, t2, 's');
    809 	return EXIT_SUCCESS;
    810 }
    811