xref: /netbsd-src/usr.bin/infocmp/infocmp.c (revision 721f832e4d2dc130111dcf735d58f325376362dc)
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
outstr(FILE * f,const char * str)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
ent_compare(const void * a,const void * b)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
setdb(char * db)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
print_ent(const TIENT * ents,size_t nents)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
load_ents(TIENT * ents,TERMINAL * t,char type)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
cprint_ent(TIENT * ent)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
compare_ents(TIENT * ents1,size_t n1,TIENT * ents2,size_t n2)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 *
load_term(const char * name)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
show_missing(TERMINAL * t1,TERMINAL * t2,char type)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 *
find_userdef(TERMINAL * term,const char * id)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
use_terms(TERMINAL * term,size_t nuse,char ** uterms)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
main(int argc,char ** argv)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