xref: /netbsd-src/usr.bin/infocmp/infocmp.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /* $NetBSD: infocmp.c,v 1.8 2013/10/01 09:01:49 roy Exp $ */
2 
3 /*
4  * Copyright (c) 2009, 2010 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.8 2013/10/01 09:01:49 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 
44 #define SW 8
45 
46 typedef struct tient {
47 	char type;
48 	const char *id;
49 	signed char flag;
50 	short num;
51 	const char *str;
52 } TIENT;
53 
54 static size_t cols;
55 static int aflag, cflag, nflag, qflag, xflag;
56 
57 static size_t
58 outstr(FILE *f, const char *str)
59 {
60 	unsigned char ch;
61 	size_t r, l;
62 
63 	r = 0;
64 	l = strlen(str);
65 	while ((ch = (unsigned char)(*str++)) != '\0') {
66 		switch (ch) {
67 		case 128:
68 			ch = '0';
69 			break;
70 		case '\033':
71 			ch = 'E';
72 			break;
73 		case '\014':
74 			ch = 'f';
75 			break;
76 		case '^': /* FALLTHROUGH */
77 		case ',': /* escape these */
78 			break;
79 		case ' ':
80 			ch = 's';
81 			break;
82 		default:
83 			if (ch == '\177') {
84 				if (f != NULL)
85 					fputc('^', f);
86 				ch = '?';
87 				r++;
88 			} else if (iscntrl(ch) &&
89 			    ch < 128 &&
90 			    ch != '\\' &&
91 			    (l < 4 || isdigit((unsigned char)*str)))
92 			{
93 				if (f != NULL)
94 					fputc('^', f);
95 				ch += '@';
96 				r++;
97 			} else if (!isprint(ch)) {
98 				if (f != NULL)
99 					fprintf(f, "\\%03o", ch);
100 				r += 4;
101 				continue;
102 			}
103 			goto prnt;
104 		}
105 
106 		if (f != NULL)
107 			fputc('\\', f);
108 		r++;
109 prnt:
110 		if (f != NULL)
111 			fputc(ch, f);
112 		r++;
113 	}
114 	return r;
115 }
116 
117 static int
118 ent_compare(const void *a, const void *b)
119 {
120 	const TIENT *ta, *tb;
121 
122 	ta = (const TIENT *)a;
123 	tb = (const TIENT *)b;
124 	return strcmp(ta->id, tb->id);
125 }
126 
127 static void
128 setdb(char *db)
129 {
130 	size_t len;
131 
132 	len = strlen(db);
133 	if (len > 3 &&
134 	    db[len - 3] == '.' &&
135 	    db[len - 2] == 'd' &&
136 	    db[len - 1] == 'b')
137 		db[len - 3] = '\0';
138 	setenv("TERMINFO", db, 1);
139 }
140 
141 static void
142 print_ent(const TIENT *ents, size_t nents)
143 {
144 	size_t col, i, l;
145 	char nbuf[64];
146 
147 	if (nents == 0)
148 		return;
149 
150 	col = SW;
151 	printf("\t");
152 	for (i = 0; i < nents; i++) {
153 		if (*ents[i].id == '.' && aflag == 0)
154 			continue;
155 		switch (ents[i].type) {
156 		case 'f':
157 			if (ents[i].flag == ABSENT_BOOLEAN)
158 				continue;
159 			l = strlen(ents[i].id) + 2;
160 			if (ents[i].flag == CANCELLED_BOOLEAN)
161 				l++;
162 			break;
163 		case 'n':
164 			if (ents[i].num == ABSENT_NUMERIC)
165 				continue;
166 			if (VALID_NUMERIC(ents[i].num))
167 				l = snprintf(nbuf, sizeof(nbuf), "%s#%d,",
168 				    ents[i].id, ents[i].num);
169 			else
170 				l = snprintf(nbuf, sizeof(nbuf), "%s@,",
171 				    ents[i].id);
172 			break;
173 		case 's':
174 			if (ents[i].str == ABSENT_STRING)
175 				continue;
176 			if (VALID_STRING(ents[i].str))
177 				l = strlen(ents[i].id) +
178 				    outstr(NULL, ents[i].str) + 7;
179 			else
180 				l = strlen(ents[i].id) + 3;
181 			break;
182 		default:
183 			errx(1, "invalid type");
184 		}
185 		if (col != SW) {
186 			if (col + l > cols) {
187 				printf("\n\t");
188 				col = SW;
189 			} else
190 				col += printf(" ");
191 		}
192 		switch (ents[i].type) {
193 		case 'f':
194 			col += printf("%s", ents[i].id);
195 			if (ents[i].flag == ABSENT_BOOLEAN ||
196 			    ents[i].flag == CANCELLED_BOOLEAN)
197 				col += printf("@");
198 			col += printf(",");
199 			break;
200 		case 'n':
201 			col += printf("%s", nbuf);
202 			break;
203 		case 's':
204 			col += printf("%s", ents[i].id);
205 			if (VALID_STRING(ents[i].str)) {
206 				col += printf("=");
207 				col += outstr(stdout, ents[i].str);
208 			} else
209 				col += printf("@");
210 			col += printf(",");
211 			break;
212 		}
213 	}
214 	printf("\n");
215 }
216 
217 static size_t
218 load_ents(TIENT *ents, TERMINAL *t, char type)
219 {
220 	size_t i, n, max;
221 	TERMUSERDEF *ud;
222 
223 	switch (type) {
224 	case 'f':
225 		max = TIFLAGMAX;
226 		break;
227 	case 'n':
228 		max = TINUMMAX;
229 		break;
230 	default:
231 		max = TISTRMAX;
232 	}
233 
234 	n = 0;
235 	for (i = 0; i <= max; i++) {
236 		switch (type) {
237 		case 'f':
238 			if (t->flags[i] == 1 ||
239 			    (aflag && t->flags[i] == CANCELLED_BOOLEAN))
240 			{
241 				ents[n].id = _ti_flagid(i);
242 				ents[n].type = 'f';
243 				ents[n++].flag = t->flags[i];
244 			}
245 			break;
246 		case 'n':
247 			if (VALID_NUMERIC(t->nums[i]) ||
248 			    (aflag && t->nums[i] == CANCELLED_NUMERIC))
249 			{
250 				ents[n].id = _ti_numid(i);
251 				ents[n].type = 'n';
252 				ents[n++].num = t->nums[i];
253 			}
254 			break;
255 		default:
256 			if (VALID_STRING(t->strs[i]) ||
257 			    (aflag && t->strs[i] == CANCELLED_STRING))
258 			{
259 				ents[n].id = _ti_strid(i);
260 				ents[n].type = 's';
261 				ents[n++].str = t->strs[i];
262 			}
263 			break;
264 		}
265 	}
266 
267 	if (xflag != 0 && t->_nuserdefs != 0) {
268 		for (i = 0; i < t->_nuserdefs; i++) {
269 			ud = &t->_userdefs[i];
270 			if (ud->type == type) {
271 				switch (type) {
272 				case 'f':
273 					if (!aflag &&
274 					    !VALID_BOOLEAN(ud->flag))
275 						continue;
276 					break;
277 				case 'n':
278 					if (!aflag &&
279 					    !VALID_NUMERIC(ud->num))
280 						continue;
281 					break;
282 				case 's':
283 					if (!aflag &&
284 					    !VALID_STRING(ud->str))
285 						continue;
286 					break;
287 				}
288 				ents[n].id = ud->id;
289 				ents[n].type = ud->type;
290 				ents[n].flag = ud->flag;
291 				ents[n].num = ud->num;
292 				ents[n++].str = ud->str;
293 			}
294 		}
295 	}
296 
297 	qsort(ents, n, sizeof(TIENT), ent_compare);
298 	return n;
299 }
300 
301 static void
302 cprint_ent(TIENT *ent)
303 {
304 
305 	if (ent == NULL) {
306 		if (qflag == 0)
307 			printf("NULL");
308 		else
309 			printf("-");
310 	}
311 
312 	switch (ent->type) {
313 	case 'f':
314 		if (VALID_BOOLEAN(ent->flag))
315 			printf(ent->flag == 1 ? "T" : "F");
316 		else if (qflag == 0)
317 			printf("F");
318 		else if (ent->flag == CANCELLED_BOOLEAN)
319 			printf("@");
320 		else
321 			printf("-");
322 		break;
323 	case 'n':
324 		if (VALID_NUMERIC(ent->num))
325 			printf("%d", ent->num);
326 		else if (qflag == 0)
327 			printf("NULL");
328 		else if (ent->num == CANCELLED_NUMERIC)
329 			printf("@");
330 		else
331 			printf("-");
332 		break;
333 	case 's':
334 		if (VALID_STRING(ent->str)) {
335 			printf("'");
336 			outstr(stdout, ent->str);
337 			printf("'");
338 		} else if (qflag == 0)
339 			printf("NULL");
340 		else if (ent->str == CANCELLED_STRING)
341 			printf("@");
342 		else
343 			printf("-");
344 		break;
345 	}
346 }
347 
348 static void
349 compare_ents(TIENT *ents1, size_t n1, TIENT *ents2, size_t n2)
350 {
351 	size_t i1, i2;
352 	TIENT *e1, *e2, ee;
353 	int c;
354 
355 	i1 = i2 = 0;
356 	ee.type = 'f';
357 	ee.flag = ABSENT_BOOLEAN;
358 	ee.num = ABSENT_NUMERIC;
359 	ee.str = ABSENT_STRING;
360 	while (i1 != n1 || i2 != n2) {
361 		if (i1 == n1)
362 			c = 1;
363 		else if (i2 == n2)
364 			c = -1;
365 		else
366 			c = strcmp(ents1[i1].id, ents2[i2].id);
367 		if (c == 0) {
368 			e1 = &ents1[i1++];
369 			e2 = &ents2[i2++];
370 		} else if (c < 0) {
371 			e1 = &ents1[i1++];
372 			e2 = &ee;
373 			ee.id = e1->id;
374 			ee.type = e1->type;
375 		} else {
376 			e1 = &ee;
377 			e2 = &ents2[i2++];
378 			ee.id = e2->id;
379 			ee.type = e2->type;
380 		}
381 		switch (e1->type) {
382 		case 'f':
383 			if (cflag != 0) {
384 				if (e1->flag == e2->flag)
385 					printf("\t%s\n", ents1[i1].id);
386 				continue;
387 			}
388 			if (e1->flag == e2->flag)
389 				continue;
390 			break;
391 		case 'n':
392 			if (cflag != 0) {
393 				if (e1->num == e2->num)
394 					printf("\t%s#%d\n",
395 					    ents1[i1].id, ents1[i1].num);
396 				continue;
397 			}
398 			if (e1->num == e2->num)
399 				continue;
400 			break;
401 		case 's':
402 			if (cflag != 0) {
403 				if (VALID_STRING(e1->str) &&
404 				    VALID_STRING(e2->str) &&
405 				    strcmp(e1->str, e2->str) == 0) {
406 					printf("\t%s=", ents1[i1].id);
407 					outstr(stdout, ents1[i1].str);
408 					printf("\n");
409 				}
410 				continue;
411 			}
412 			if (VALID_STRING(e1->str) &&
413 			    VALID_STRING(e2->str) &&
414 			    strcmp(e1->str, e2->str) == 0)
415 				continue;
416 			break;
417 		}
418 		printf("\t%s: ", e1->id);
419 		cprint_ent(e1);
420 		if (e1->type == 'f')
421 			printf(":");
422 		else
423 			printf(", ");
424 		cprint_ent(e2);
425 		printf(".\n");
426 	}
427 }
428 
429 static TERMINAL *
430 load_term(const char *name)
431 {
432 	TERMINAL *t;
433 
434 	t = calloc(1, sizeof(*t));
435 	if (t == NULL)
436 		err(1, "calloc");
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(1, "no terminal definition found in internal database");
446 	else
447 		errx(1, "no terminal definition found in %s.db", _ti_database);
448 }
449 
450 static void
451 show_missing(TERMINAL *t1, TERMINAL *t2, char type)
452 {
453 	ssize_t i, max;
454 	const char *id;
455 
456 	switch (type) {
457 	case 'f':
458 		max = TIFLAGMAX;
459 		break;
460 	case 'n':
461 		max = TINUMMAX;
462 		break;
463 	default:
464 		max = TISTRMAX;
465 	}
466 
467 	for (i = 0; i <= max; i++) {
468 		switch (type) {
469 		case 'f':
470 			if (t1->flags[i] != ABSENT_BOOLEAN ||
471 			    t2->flags[i] != ABSENT_BOOLEAN)
472 				continue;
473 			id = _ti_flagid(i);
474 			break;
475 		case 'n':
476 			if (t1->nums[i] != ABSENT_NUMERIC ||
477 			    t2->nums[i] != ABSENT_NUMERIC)
478 				continue;
479 			id = _ti_numid(i);
480 			break;
481 		default:
482 			if (t1->strs[i] != ABSENT_STRING ||
483 			    t2->strs[i] != ABSENT_STRING)
484 				continue;
485 			id = _ti_strid(i);
486 			break;
487 		}
488 		printf("\t!%s.\n", id);
489 	}
490 }
491 
492 static TERMUSERDEF *
493 find_userdef(TERMINAL *term, const char *id)
494 {
495 	size_t i;
496 
497 	for (i = 0; i < term->_nuserdefs; i++)
498 		if (strcmp(term->_userdefs[i].id, id) == 0)
499 			return &term->_userdefs[i];
500 	return NULL;
501 }
502 
503 static void
504 use_terms(TERMINAL *term, size_t nuse, char **uterms)
505 {
506 	TERMINAL **terms;
507 	TERMUSERDEF *ud, *tud;
508 	size_t i, j, agree, absent, data;
509 
510 	terms = malloc(sizeof(**terms) * nuse);
511 	if (terms == NULL)
512 		err(1, "malloc");
513 	for (i = 0; i < nuse; i++) {
514 		if (strcmp(term->name, *uterms) == 0)
515 			errx(1, "cannot use same terminal");
516 		for (j = 0; j < i; j++)
517 			if (strcmp(terms[j]->name, *uterms) == 0)
518 				errx(1, "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 = realloc(term->_userdefs,
632 			    sizeof(*term->_userdefs) * (term->_nuserdefs + 1));
633 			if (term->_userdefs == NULL)
634 				err(1, "malloc");
635 			tud = &term->_userdefs[term->_nuserdefs++];
636 			tud->id = terms[i]->_userdefs[j].id;
637 			tud->type = terms[i]->_userdefs[j].flag;
638 			tud->flag = CANCELLED_BOOLEAN;
639 			tud->num = CANCELLED_NUMERIC;
640 			tud->str = CANCELLED_STRING;
641 		}
642 	}
643 }
644 
645 int
646 main(int argc, char **argv)
647 {
648 	char *term, *Barg;
649 	int ch, uflag;
650 	TERMINAL *t, *t2;
651 	size_t n, n2;
652 	struct winsize ws;
653 	TIENT ents[TISTRMAX + 1], ents2[TISTRMAX + 1];
654 
655 	cols = 80; /* default */
656 	term = getenv("COLUMNS");
657 	if (term != NULL)
658 		cols = strtoul(term, NULL, 10);
659 	else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
660 		cols = ws.ws_col;
661 
662 	uflag = xflag = 0;
663 	Barg = NULL;
664 	while ((ch = getopt(argc, argv, "1A:B:acnquw:x")) != -1)
665 		switch (ch) {
666 		case '1':
667 			cols = 1;
668 			break;
669 		case 'A':
670 			setdb(optarg);
671 			break;
672 		case 'B':
673 			Barg = optarg;
674 			break;
675 		case 'a':
676 			aflag = 1;
677 			break;
678 		case 'c':
679 			cflag = 1;
680 			break;
681 		case 'n':
682 			nflag = 1;
683 			break;
684 		case 'q':
685 			qflag = 1;
686 			break;
687 		case 'u':
688 			uflag = 1;
689 			aflag = 1;
690 			break;
691 		case 'w':
692 			cols = strtoul(optarg, NULL, 10);
693 			break;
694 		case 'x':
695 			xflag = 1;
696 			break;
697 		case '?':
698 		default:
699 			fprintf(stderr,
700 			    "usage: %s [-1acnqux] [-A database] [-B database] "
701 			    "[-w cols] [term]\n",
702 			    getprogname());
703 			return EXIT_FAILURE;
704 		}
705 	cols--;
706 
707 	if (optind + 1 < argc)
708 		aflag = 1;
709 
710 	if (optind < argc)
711 		term = argv[optind++];
712 	else
713 		term = NULL;
714 	t = load_term(term);
715 
716 	if (uflag != 0)
717 		use_terms(t, argc - optind, argv + optind);
718 
719 	if ((optind + 1 != argc && nflag == 0) || uflag != 0) {
720 		if (uflag == 0) {
721 			printf("# Reconstructed from ");
722 			if (_ti_database == NULL)
723 				printf("internal database\n");
724 			else
725 				printf("%s%s\n", _ti_database,
726 				    *_ti_database == '/' ? ".cdb" : "");
727 		}
728 		printf("%s", t->name);
729 		if (t->_alias != NULL && *t->_alias != '\0')
730 			printf("|%s", t->_alias);
731 		if (t->desc != NULL && *t->desc != '\0')
732 			printf("|%s", t->desc);
733 		printf(",\n");
734 
735 		n = load_ents(ents, t, 'f');
736 		print_ent(ents, n);
737 		n = load_ents(ents, t, 'n');
738 		print_ent(ents, n);
739 		n = load_ents(ents, t, 's');
740 		print_ent(ents, n);
741 
742 		if (uflag != 0) {
743 			printf("\t");
744 			n = SW;
745 			for (; optind < argc; optind++) {
746 				n2 = 5 + strlen(argv[optind]);
747 				if (n != SW) {
748 					if (n + n2 > cols) {
749 						printf("\n\t");
750 						n = SW;
751 					} else
752 						n += printf(" ");
753 				}
754 				n += printf("use=%s,", argv[optind]);
755 			}
756 			printf("\n");
757 		}
758 		return EXIT_SUCCESS;
759 	}
760 
761 	if (Barg == NULL)
762 		unsetenv("TERMINFO");
763 	else
764 		setdb(Barg);
765 	t2 = load_term(argv[optind++]);
766 	printf("comparing %s to %s.\n", t->name, t2->name);
767 	if (qflag == 0)
768 		printf("    comparing booleans.\n");
769 	if (nflag == 0) {
770 		n = load_ents(ents, t, 'f');
771 		n2 = load_ents(ents2, t2, 'f');
772 		compare_ents(ents, n, ents2, n2);
773 	} else
774 		show_missing(t, t2, 'f');
775 	if (qflag == 0)
776 		printf("    comparing numbers.\n");
777 	if (nflag == 0) {
778 		n = load_ents(ents, t, 'n');
779 		n2 = load_ents(ents2, t2, 'n');
780 		compare_ents(ents, n, ents2, n2);
781 	} else
782 		show_missing(t, t2, 'n');
783 	if (qflag == 0)
784 		printf("    comparing strings.\n");
785 	if (nflag == 0) {
786 		n = load_ents(ents, t, 's');
787 		n2 = load_ents(ents2, t2, 's');
788 		compare_ents(ents, n, ents2, n2);
789 	} else
790 		show_missing(t, t2, 's');
791 	return EXIT_SUCCESS;
792 }
793