xref: /illumos-gate/usr/src/cmd/infocmp/infocmp.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2019, Joyent, Inc.
31  */
32 
33 /*
34     NAME
35 	infocmp - compare terminfo descriptions, or dump a terminfo
36 	description
37 
38     AUTHOR
39 	Tony Hansen, February 23, 1984.
40 */
41 
42 #include "curses.h"
43 #include "term.h"
44 #include "print.h"
45 #include <fcntl.h>
46 #include <stdlib.h>
47 
48 /* externs from libcurses */
49 extern char *boolnames[];
50 extern char *boolcodes[];
51 extern char *boolfnames[];
52 extern char *numnames[];
53 extern char *numcodes[];
54 extern char *numfnames[];
55 extern char *strnames[];
56 extern char *strcodes[];
57 extern char *strfnames[];
58 extern char ttytype[];
59 extern int tgetflag();
60 extern int tgetnum();
61 extern char *tgetstr();
62 
63 /* externs from libc */
64 extern void exit();
65 extern void qsort();
66 extern char *getenv();
67 extern int getopt();
68 extern int optind;
69 extern char *optarg;
70 extern char *strncpy(), *strcpy();
71 extern int strcmp(), strlen();
72 
73 /* data structures for this program */
74 
75 struct boolstruct {
76     char *infoname;			/* the terminfo capability name */
77     char *capname;			/* the termcap capability name */
78     char *fullname;			/* the long C variable name */
79     char *secondname;			/* the use= terminal w/ this value */
80     char val;				/* the value */
81     char secondval;			/* the value in the use= terminal */
82     char changed;			/* a use= terminal changed the value */
83     char seenagain;			/* a use= terminal had this entry */
84     };
85 
86 struct numstruct {
87     char *infoname;			/* ditto from above */
88     char *capname;
89     char *fullname;
90     char *secondname;
91     short val;
92     short secondval;
93     char changed;
94     char seenagain;
95     };
96 
97 struct strstruct {
98     char *infoname;			/* ditto from above */
99     char *capname;
100     char *fullname;
101     char *secondname;
102     char *val;
103     char *secondval;
104     char changed;
105     char seenagain;
106     };
107 
108 /* globals for this file */
109 char *progname;			/* argv[0], the name of the program */
110 static struct boolstruct *ibool; /* array of char information */
111 static struct numstruct *num;	/* array of number information */
112 static struct strstruct *str;	/* array of string information */
113 static char *used;		/* usage statistics */
114 static int numbools;		/* how many booleans there are */
115 static int numnums;		/* how many numbers there are */
116 static int numstrs;		/* how many strings there are */
117 #define	TTYLEN 255
118 static char *firstterm;		/* the name of the first terminal */
119 static char *savettytype;	/* the synonyms of the first terminal */
120 static char _savettytype[TTYLEN+1]; /* the place to save those names */
121 static int devnull;		/* open("/dev/null") for setupterm */
122 #define	trace stderr		/* send trace messages to stderr */
123 
124 /* options */
125 static int verbose = 0;		/* debugging printing level */
126 static int diff = 0;		/* produce diff listing, the default */
127 static int common = 0;		/* produce common listing */
128 static int neither = 0;		/* list caps in neither entry */
129 static int use = 0;		/* produce use= comparison listing */
130 static enum printtypes printing	/* doing any of above printing at all */
131 	= pr_none;
132 enum { none, by_database, by_terminfo, by_longnames, by_cap }
133     sortorder = none;		/* sort the fields for printing */
134 static char *term1info, *term2info;	/* $TERMINFO settings */
135 static int Aflag = 0, Bflag = 0;	/* $TERMINFO was set with -A/-B */
136 
137 #define	EQUAL(s1, s2)	(((s1 == NULL) && (s2 == NULL)) || \
138 			((s1 != NULL) && (s2 != NULL) && \
139 			(strcmp(s1, s2) == 0)))
140 
141 static void sortnames();
142 int numcompare(const void *, const void *);
143 int boolcompare(const void *, const void *);
144 int strcompare(const void *, const void *);
145 static void check_nth_terminal(char *, int);
146 
147 void
148 badmalloc()
149 {
150 	(void) fprintf(stderr, "%s: malloc is out of space!\n", progname);
151 	exit(-1);
152 }
153 
154 /*
155     Allocate and initialize the global data structures and variables.
156 */
157 void
158 allocvariables(int argc, int firstoptind)
159 {
160 	register int i, nullseen;
161 
162 	/* find out how many names we are dealing with */
163 	for (numbools = 0; boolnames[numbools]; numbools++)
164 		;
165 	for (numnums = 0; numnames[numnums]; numnums++)
166 		;
167 	for (numstrs = 0; strnames[numstrs]; numstrs++)
168 		;
169 
170 	if (verbose) {
171 		(void) fprintf(trace, "There are %d boolean capabilities.\n",
172 		    numbools);
173 		(void) fprintf(trace, "There are %d numeric capabilities.\n",
174 		    numnums);
175 		(void) fprintf(trace, "There are %d string capabilities.\n",
176 		    numstrs);
177 	}
178 
179 	/* Allocate storage for the names and their values */
180 	ibool = (struct boolstruct  *) malloc((unsigned) numbools *
181 	    sizeof (struct boolstruct));
182 	num = (struct numstruct *) malloc((unsigned) numnums *
183 	    sizeof (struct numstruct));
184 	str = (struct strstruct *) malloc((unsigned) numstrs *
185 	    sizeof (struct strstruct));
186 
187 	/* Allocate array to keep track of which names have been used. */
188 	if (use) {
189 		used = (char *) malloc((unsigned) (argc - firstoptind) *
190 		    sizeof (char));
191 	}
192 
193 	if ((ibool == NULL) || (num == NULL) || (str == NULL) ||
194 	    (use && (used == NULL)))
195 		badmalloc();
196 
197 	/* Fill in the names and initialize the structures. */
198 	nullseen = FALSE;
199 	for (i = 0; i < numbools; i++) {
200 		ibool[i].infoname = boolnames[i];
201 		ibool[i].capname = boolcodes[i];
202 		/* This is necessary until fnames.c is */
203 		/* incorporated into standard curses. */
204 		if (nullseen || (boolfnames[i] == NULL)) {
205 			ibool[i].fullname = "unknown_boolean";
206 			nullseen = TRUE;
207 		} else {
208 			ibool[i].fullname = boolfnames[i];
209 		}
210 		ibool[i].changed = FALSE;
211 		ibool[i].seenagain = FALSE;
212 	}
213 	nullseen = 0;
214 	for (i = 0; i < numnums; i++) {
215 		num[i].infoname = numnames[i];
216 		num[i].capname = numcodes[i];
217 		if (nullseen || (numfnames[i] == NULL)) {
218 			ibool[i].fullname = "unknown_number";
219 			nullseen = TRUE;
220 		} else {
221 			num[i].fullname = numfnames[i];
222 		}
223 		num[i].changed = FALSE;
224 		num[i].seenagain = FALSE;
225 	}
226 	nullseen = 0;
227 	for (i = 0; i < numstrs; i++) {
228 		str[i].infoname = strnames[i];
229 		str[i].capname = strcodes[i];
230 		if (nullseen || (strfnames[i] == NULL)) {
231 			str[i].fullname = "unknown_string";
232 			nullseen = TRUE;
233 		} else {
234 			str[i].fullname = strfnames[i];
235 		}
236 		str[i].changed = FALSE;
237 		str[i].seenagain = FALSE;
238 	}
239 }
240 
241 /*
242 	Routines to be passed to qsort(3) for comparison of the structures.
243 */
244 int
245 boolcompare(const void *x, const void *y)
246 {
247 	struct boolstruct *a;
248 	struct boolstruct *b;
249 
250 	a = (struct boolstruct *)x;
251 	b = (struct boolstruct *)y;
252 
253 	switch ((int) sortorder) {
254 		case (int) by_terminfo:
255 			return (strcmp(a->infoname, b->infoname));
256 		case (int) by_cap:
257 			return (strcmp(a->capname, b->capname));
258 		case (int) by_longnames:
259 			return (strcmp(a->fullname, b->fullname));
260 		default:
261 			return (0);
262 	}
263 }
264 
265 int
266 numcompare(const void *x, const void *y)
267 {
268 	struct numstruct *a;
269 	struct numstruct *b;
270 
271 	a = (struct numstruct *)x;
272 	b = (struct numstruct *)y;
273 	switch ((int) sortorder) {
274 		case (int) by_terminfo:
275 			return (strcmp(a->infoname, b->infoname));
276 		case (int) by_cap:
277 			return (strcmp(a->capname, b->capname));
278 		case (int) by_longnames:
279 			return (strcmp(a->fullname, b->fullname));
280 		default:
281 			return (0);
282 	}
283 }
284 
285 int
286 strcompare(const void *x, const void *y)
287 {
288 	struct strstruct *a;
289 	struct strstruct *b;
290 
291 	a = (struct strstruct *)x;
292 	b = (struct strstruct *)y;
293 
294 	switch ((int) sortorder) {
295 		case (int) by_terminfo:
296 			return (strcmp(a->infoname, b->infoname));
297 		case (int) by_cap:
298 			return (strcmp(a->capname, b->capname));
299 		case (int) by_longnames:
300 			return (strcmp(a->fullname, b->fullname));
301 		default:
302 			return (0);
303 	}
304 }
305 
306 /*
307 	Sort the entries by their terminfo name.
308 */
309 static void
310 sortnames()
311 {
312 	if (sortorder != by_database) {
313 		qsort((char *) ibool, (unsigned) numbools,
314 			sizeof (struct boolstruct), boolcompare);
315 		qsort((char *) num, (unsigned) numnums,
316 			sizeof (struct numstruct), numcompare);
317 		qsort((char *) str, (unsigned) numstrs,
318 			sizeof (struct strstruct), strcompare);
319 	}
320 	return;
321 }
322 
323 /*
324 	Print out a string, or "NULL" if it's not defined.
325 */
326 void
327 PR(FILE *stream, char *string)
328 {
329 	if (string == NULL)
330 		(void) fprintf(stream, "NULL");
331 	else
332 		tpr(stream, string);
333 }
334 
335 /*
336 	Output the 'ko' termcap string. This is a list of all of the input
337 	keys that input the same thing as the corresponding output strings.
338 */
339 int kncounter;
340 char kobuffer[512];
341 
342 char
343 *addko(char *output, char *input, char *koptr)
344 {
345 	char *inptr, *outptr, padbuffer[512];
346 	inptr = tgetstr(input, (char **)0);
347 	if (inptr == NULL)
348 		return (koptr);
349 	outptr = tgetstr(output, (char **)0);
350 	if (outptr == NULL)
351 		return (koptr);
352 	outptr = rmpadding(outptr, padbuffer, (int *) 0);
353 	if (strcmp(inptr, outptr) == 0) {
354 		*koptr++ = *output++;
355 		*koptr++ = *output++;
356 		*koptr++ = ',';
357 		kncounter++;
358 	}
359 	return (koptr);
360 }
361 
362 void
363 setupknko()
364 {
365 	char *koptr;
366 
367 	kncounter = 0;
368 	koptr = kobuffer;
369 
370 	koptr = addko("bs", "kb", koptr);	/* key_backspace */
371 	koptr = addko("bt", "kB", koptr);	/* key_btab */
372 	koptr = addko("cl", "kC", koptr);	/* key_clear */
373 	koptr = addko("le", "kl", koptr);	/* key_left */
374 	koptr = addko("do", "kd", koptr);	/* key_down */
375 	koptr = addko("nd", "kr", koptr);	/* key_right */
376 	koptr = addko("up", "ku", koptr);	/* key_up */
377 	koptr = addko("dc", "kD", koptr);	/* key_dc */
378 	koptr = addko("dl", "kL", koptr);	/* key_dl */
379 	koptr = addko("cd", "kS", koptr);	/* key_eos */
380 	koptr = addko("ce", "kE", koptr);	/* key_eol */
381 	koptr = addko("ho", "kh", koptr);	/* key_home */
382 	koptr = addko("st", "kT", koptr);	/* key_stab */
383 	koptr = addko("ic", "kI", koptr);	/* key_ic */
384 	koptr = addko("im", "kI", koptr);	/* key_ic */
385 	koptr = addko("al", "kA", koptr);	/* key_il */
386 	koptr = addko("sf", "kF", koptr);	/* key_sf */
387 	koptr = addko("ll", "kH", koptr);	/* key_ll */
388 	koptr = addko("sr", "kR", koptr);	/* key_sr */
389 	koptr = addko("ei", "kM", koptr);	/* key_eic */
390 	koptr = addko("ct", "ka", koptr);	/* key_catab */
391 
392 	/* get rid of comma */
393 	if (koptr != kobuffer)
394 		*(--koptr) = '\0';
395 }
396 
397 void
398 pr_kn()
399 {
400 	if (kncounter > 0)
401 		pr_number((char *)0, "kn", (char *)0, kncounter);
402 }
403 
404 void
405 pr_ko()
406 {
407 	if (kncounter > 0)
408 		pr_string((char *)0, "ko", (char *)0, kobuffer);
409 }
410 
411 void
412 pr_bcaps()
413 {
414 	char *retptr;
415 	char padbuffer[512];
416 
417 	if (verbose)
418 		(void) fprintf(trace, "looking at 'bs'\n");
419 	retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
420 	if (strcmp("\\b", retptr) == 0)
421 		pr_boolean((char *)0, "bs", (char *)0, 1);
422 
423 	if (verbose)
424 		(void) fprintf(trace, "looking at 'pt'\n");
425 	retptr = cconvert(rmpadding(tab, padbuffer, (int *) 0));
426 	if (strcmp("\\t", retptr) == 0)
427 		pr_boolean((char *)0, "pt", (char *)0, 1);
428 
429 	if (verbose)
430 		(void) fprintf(trace, "looking at 'nc'\n");
431 	retptr = cconvert(rmpadding(carriage_return, padbuffer, (int *) 0));
432 	if (strcmp("\\r", retptr) != 0)
433 		pr_boolean((char *)0, "nc", (char *)0, 1);
434 
435 	if (verbose)
436 		(void) fprintf(trace, "looking at 'ns'\n");
437 	if (scroll_forward == NULL)
438 		pr_boolean((char *)0, "ns", (char *)0, 1);
439 
440 	/* Ignore "xr": Return acts like ce \r \n (Delta Data) */
441 }
442 
443 void
444 pr_ncaps()
445 {
446 	char padbuffer[512];
447 	int padding;
448 
449 	if (verbose)
450 		(void) fprintf(trace, "looking at 'ug'\n");
451 	/* Duplicate sg for ug: Number of blank chars left by us or ue */
452 	if (magic_cookie_glitch > -1)
453 		pr_number((char *)0, "ug", (char *)0, magic_cookie_glitch);
454 
455 	if (verbose)
456 		(void) fprintf(trace, "looking at 'dB'\n");
457 	/* Number of millisec of bs delay needed */
458 	(void) rmpadding(cursor_left, padbuffer, &padding);
459 	if (padding > 0)
460 		pr_number((char *)0, "dB", (char *)0, padding);
461 
462 	if (verbose)
463 		(void) fprintf(trace, "looking at 'dC'\n");
464 	/* Number of millisec of cr delay needed */
465 	(void) rmpadding(carriage_return, padbuffer, &padding);
466 	if (padding > 0)
467 		pr_number((char *)0, "dC", (char *)0, padding);
468 
469 	if (verbose)
470 		(void) fprintf(trace, "looking at 'dF'\n");
471 	/* Number of millisec of ff delay needed */
472 	(void) rmpadding(form_feed, padbuffer, &padding);
473 	if (padding > 0)
474 		pr_number((char *)0, "dF", (char *)0, padding);
475 
476 	if (verbose)
477 		(void) fprintf(trace, "looking at 'dN'\n");
478 	/* Number of millisec of nl delay needed */
479 	(void) rmpadding(cursor_down, padbuffer, &padding);
480 	if (padding > 0)
481 		pr_number((char *)0, "dN", (char *)0, padding);
482 
483 	if (verbose)
484 		(void) fprintf(trace, "looking at 'dT'\n");
485 	/* Number of millisec of tab delay needed */
486 	(void) rmpadding(tab, padbuffer, &padding);
487 	if (padding > 0)
488 		pr_number((char *)0, "dT", (char *)0, padding);
489 
490 	/* Handle "kn": Number of "other" keys */
491 	setupknko();
492 	pr_kn();
493 }
494 
495 void
496 pr_scaps()
497 {
498 	char *retptr;
499 	char padbuffer[512];
500 
501 	/* Backspace if not "^H" */
502 	if (verbose)
503 		(void) fprintf(trace, "looking at 'bc'\n");
504 	retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
505 	if (strcmp("\\b", retptr) != 0)
506 		pr_string((char *)0, "bc", (char *)0, cursor_left);
507 
508 	/* Newline character (default "\n") */
509 	if (verbose)
510 		(void) fprintf(trace, "looking at 'nl'\n");
511 	retptr = cconvert(rmpadding(cursor_down, padbuffer, (int *) 0));
512 	if (strcmp("\\n", retptr) != 0)
513 		pr_string((char *)0, "nl", (char *)0, cursor_down);
514 
515 	/* Handle "ko" here: Termcap entries for other non-function keys */
516 	pr_ko();
517 
518 	/* Ignore "ma": Arrow key map, used by vi version 2 only */
519 }
520 
521 /*
522 	Set up the first terminal and save the values from it.
523 */
524 void
525 initfirstterm(char *term)
526 {
527 	register int i;
528 
529 	if (verbose) {
530 		(void) fprintf(trace, "setting up terminal type '%s'.\n",
531 		    term);
532 	}
533 
534 	(void) setupterm(term, devnull, (int *) 0);
535 
536 	/* Save the name for later use. */
537 	if (use) {
538 		register unsigned int length;
539 		savettytype = _savettytype;
540 		if ((length = strlen(ttytype)) >= TTYLEN) {
541 			savettytype = malloc(length);
542 			if (savettytype == NULL) {
543 				(void) fprintf(stderr, "%s: malloc is out "
544 				    "of space\n", progname);
545 				(void) strncpy(_savettytype, ttytype,
546 				    TTYLEN-1);
547 				_savettytype[TTYLEN] = '\0';
548 				savettytype = _savettytype;
549 			}
550 		} else {
551 			(void) strcpy(_savettytype, ttytype);
552 		}
553 	}
554 
555 	if (printing != pr_none) {
556 		pr_heading(term, ttytype);
557 		pr_bheading();
558 	}
559 
560 	/* Save the values for the first terminal. */
561 	for (i = 0; i < numbools; i++) {
562 		if ((ibool[i].val = tgetflag(ibool[i].capname)) &&
563 		    printing != pr_none) {
564 			pr_boolean(ibool[i].infoname, ibool[i].capname,
565 			    ibool[i].fullname, 1);
566 		}
567 
568 		if (verbose) {
569 			(void) fprintf(trace, "%s=%d.\n", ibool[i].infoname,
570 			    ibool[i].val);
571 		}
572 	}
573 
574 	if (printing != pr_none) {
575 		if (printing == pr_cap)
576 			pr_bcaps();
577 		pr_bfooting();
578 		pr_nheading();
579 	}
580 
581 	for (i = 0; i < numnums; i++) {
582 		if (((num[i].val = tgetnum(num[i].capname)) > -1) &&
583 		    printing != pr_none) {
584 			pr_number(num[i].infoname, num[i].capname,
585 			    num[i].fullname, num[i].val);
586 		}
587 
588 		if (verbose) {
589 			(void) fprintf(trace, "%s=%d.\n", num[i].infoname,
590 			    num[i].val);
591 		}
592 	}
593 
594 	if (printing != pr_none) {
595 		if (printing == pr_cap)
596 			pr_ncaps();
597 		pr_nfooting();
598 		pr_sheading();
599 	}
600 
601 	for (i = 0; i < numstrs; i++) {
602 		str[i].val = tgetstr(str[i].capname, (char **)0);
603 		if ((str[i].val != NULL) && printing != pr_none) {
604 			pr_string(str[i].infoname, str[i].capname,
605 			    str[i].fullname, str[i].val);
606 		}
607 
608 		if (verbose) {
609 			(void) fprintf(trace, "%s='", str[i].infoname);
610 			PR(trace, str[i].val);
611 			(void) fprintf(trace, "'.\n");
612 		}
613 	}
614 
615 	if (printing == pr_cap)
616 		pr_scaps();
617 
618 	if (printing != pr_none)
619 		pr_sfooting();
620 }
621 
622 /*
623 	Set up the n'th terminal.
624 */
625 static void
626 check_nth_terminal(char *nterm, int n)
627 {
628 	register char boolval;
629 	register short numval;
630 	register char *strval;
631 	register int i;
632 
633 	if (use)
634 		used[n] = FALSE;
635 
636 	if (verbose) {
637 		(void) fprintf(trace, "adding in terminal type '%s'.\n",
638 		    nterm);
639 	}
640 
641 	(void) setupterm(nterm, devnull, (int *) 0);
642 
643 	if (printing != pr_none) {
644 		pr_heading(nterm, ttytype);
645 		pr_bheading();
646 	}
647 
648 	if (diff || common || neither) {
649 		if (Aflag && Bflag)
650 			(void) printf("comparing %s (TERMINFO=%s) to %s "
651 			    "(TERMINFO=%s).\n",
652 			firstterm, term1info, nterm, term2info);
653 		else if (Aflag)
654 			(void) printf("comparing %s (TERMINFO=%s) to %s.\n",
655 			    firstterm, term1info, nterm);
656 		else if (Bflag)
657 			(void) printf("comparing %s to %s (TERMINFO=%s).\n",
658 			    firstterm, nterm, term2info);
659 		else
660 			(void) printf("comparing %s to %s.\n",
661 			    firstterm, nterm);
662 		(void) printf("    comparing booleans.\n");
663 	}
664 
665 	/* save away the values for the nth terminal */
666 	for (i = 0; i < numbools; i++) {
667 		boolval = tgetflag(ibool[i].capname);
668 		if (use) {
669 			if (ibool[i].seenagain) {
670 			/*
671 			** We do not have to worry about this impossible case
672 			** since booleans can have only two values: true and
673 			** false.
674 			** if (boolval && (boolval != ibool[i].secondval))
675 			**  {
676 			**  (void) fprintf(trace, "use= order dependency"
677 			**  "found:\n");
678 			**  (void) fprintf(trace, "    %s: %s has %d, %s has"
679 			**   " %d.\n",
680 			**	ibool[i].capname, ibool[i].secondname,
681 			**	ibool[i].secondval, nterm, boolval);
682 			**  }
683 			*/
684 			} else {
685 				if (boolval == TRUE) {
686 					ibool[i].seenagain = TRUE;
687 					ibool[i].secondval = boolval;
688 					ibool[i].secondname = nterm;
689 					if (ibool[i].val != boolval)
690 						ibool[i].changed = TRUE;
691 					else
692 						used[n] = TRUE;
693 				}
694 			}
695 		}
696 		if (boolval) {
697 			if (printing != pr_none) {
698 				pr_boolean(ibool[i].infoname, ibool[i].capname,
699 				    ibool[i].fullname, 1);
700 			}
701 
702 			if (common && (ibool[i].val == boolval))
703 				(void) printf("\t%s= T.\n", ibool[i].infoname);
704 		} else if (neither && !ibool[i].val) {
705 			(void) printf("\t!%s.\n", ibool[i].infoname);
706 		}
707 		if (diff && (ibool[i].val != boolval))
708 			(void) printf("\t%s: %c:%c.\n", ibool[i].infoname,
709 			    ibool[i].val?'T':'F', boolval?'T':'F');
710 		if (verbose) {
711 			(void) fprintf(trace, "%s: %d:%d, changed=%d, "
712 			    "seen=%d.\n", ibool[i].infoname, ibool[i].val,
713 			    boolval, ibool[i].changed, ibool[i].seenagain);
714 		}
715 	}
716 
717 	if (printing != pr_none) {
718 		if (printing == pr_cap)
719 			pr_bcaps();
720 		pr_bfooting();
721 		pr_nheading();
722 	}
723 
724 	if (diff || common || neither)
725 		(void) printf("    comparing numbers.\n");
726 
727 	for (i = 0; i < numnums; i++) {
728 		numval = tgetnum(num[i].capname);
729 		if (use) {
730 			if (num[i].seenagain) {
731 				if ((numval > -1) &&
732 				    (numval != num[i].secondval)) {
733 					(void) fprintf(stderr,
734 					    "%s: use = order dependency "
735 					    "found:\n", progname);
736 					(void) fprintf(stderr, "    %s: %s "
737 					    "has %d, %s has %d.\n",
738 					    num[i].capname, num[i].secondname,
739 					    num[i].secondval, nterm, numval);
740 				}
741 			} else {
742 				if (numval > -1) {
743 					num[i].seenagain = TRUE;
744 					num[i].secondval = numval;
745 					num[i].secondname = nterm;
746 					if ((numval > -1) &&
747 					    (num[i].val != numval))
748 						num[i].changed = TRUE;
749 					else
750 						used[n] = TRUE;
751 				}
752 			}
753 		}
754 		if (numval > -1) {
755 			if (printing != pr_none) {
756 				pr_number(num[i].infoname, num[i].capname,
757 				    num[i].fullname, numval);
758 			}
759 
760 			if (common && (num[i].val == numval)) {
761 				(void) printf("\t%s= %d.\n", num[i].infoname,
762 				    numval);
763 			}
764 
765 		} else if (neither && (num[i].val == -1)) {
766 			(void) printf("\t!%s.\n", num[i].infoname);
767 		}
768 
769 		if (diff && (num[i].val != numval)) {
770 			(void) printf("\t%s: %d:%d.\n",
771 			    num[i].infoname, num[i].val, numval);
772 		}
773 
774 		if (verbose) {
775 			(void) fprintf(trace, "%s: %d:%d, "
776 			    "changed = %d, seen = %d.\n",
777 			    num[i].infoname, num[i].val, numval,
778 			    num[i].changed, num[i].seenagain);
779 		}
780 	}
781 
782 	if (printing != pr_none) {
783 		if (printing == pr_cap)
784 			pr_ncaps();
785 		pr_nfooting();
786 		pr_sheading();
787 	}
788 
789 	if (diff || common || neither)
790 		(void) printf("    comparing strings.\n");
791 
792 	for (i = 0; i < numstrs; i++) {
793 		strval = tgetstr(str[i].capname, (char **)0);
794 		if (use) {
795 			if (str[i].seenagain && (strval != NULL)) {
796 				if (!EQUAL(strval, str[i].secondval)) {
797 					(void) fprintf(stderr,
798 					    "use= order dependency"
799 					    "  found:\n");
800 					(void) fprintf(stderr,
801 					    "    %s: %s has '",
802 					    str[i].capname, str[i].secondname);
803 					PR(stderr, str[i].secondval);
804 					(void) fprintf(stderr,
805 					    "', %s has '", nterm);
806 					PR(stderr, strval);
807 					(void) fprintf(stderr, "'.\n");
808 				}
809 			} else {
810 				if (strval != NULL) {
811 					str[i].seenagain = TRUE;
812 					str[i].secondval = strval;
813 					str[i].secondname = nterm;
814 					if (!EQUAL(str[i].val, strval))
815 						str[i].changed = TRUE;
816 					else
817 						used[n] = TRUE;
818 				}
819 			}
820 		}
821 		if (strval != NULL) {
822 			if (printing != pr_none) {
823 				pr_string(str[i].infoname, str[i].capname,
824 				    str[i].fullname, strval);
825 			}
826 
827 			if (common && EQUAL(str[i].val, strval)) {
828 				(void) printf("\t%s= '", str[i].infoname);
829 				PR(stdout, strval);
830 				(void) printf("'.\n");
831 			}
832 		} else if (neither && (str[i].val == NULL))
833 			(void) printf("\t!%s.\n", str[i].infoname);
834 		if (diff && !EQUAL(str[i].val, strval)) {
835 			(void) printf("\t%s: '", str[i].infoname);
836 			PR(stdout, str[i].val);
837 			(void) printf("','");
838 			PR(stdout, strval);
839 			(void) printf("'.\n");
840 		}
841 		if (verbose) {
842 			(void) fprintf(trace, "%s: '", str[i].infoname);
843 			PR(trace, str[i].val);
844 			(void) fprintf(trace, "':'");
845 			PR(trace, strval);
846 			(void) fprintf(trace, "',changed=%d,seen=%d.\n",
847 			    str[i].changed, str[i].seenagain);
848 		}
849 	}
850 
851 	if (printing == pr_cap)
852 		pr_scaps();
853 
854 	if (printing != pr_none)
855 		pr_sfooting();
856 
857 	return;
858 }
859 
860 /*
861 	A capability gets an at-sign if it no longer exists, but
862 	one of the relative entries contains a value for it.
863 	It gets printed if the original value is not seen in ANY
864 	of the relative entries, or if the FIRST relative entry that has
865 	the capability gives a DIFFERENT value for the capability.
866 */
867 void
868 dorelative(int firstoptind, int argc, char **argv)
869 {
870 	register int i;
871 
872 	/* turn off printing of termcap and long names */
873 	pr_init(pr_terminfo);
874 
875 	/* print out the entry name */
876 	pr_heading((char *)0, savettytype);
877 
878 	pr_bheading();
879 
880 	/* Print out all bools that are different. */
881 	for (i = 0; i < numbools; i++) {
882 		if (!ibool[i].val && ibool[i].changed) {
883 			pr_boolean(ibool[i].infoname, (char *)0,
884 			    (char *)0, -1);
885 		} else if (ibool[i].val && (ibool[i].changed ||
886 		    !ibool[i].seenagain)) {
887 			pr_boolean(ibool[i].infoname, (char *)0, (char *)0, 1);
888 		}
889 	}
890 
891 	pr_bfooting();
892 	pr_nheading();
893 
894 	/* Print out all nums that are different. */
895 	for (i = 0; i < numnums; i++) {
896 		if (num[i].val < 0 && num[i].changed) {
897 			pr_number(num[i].infoname, (char *)0, (char *)0, -1);
898 		} else if (num[i].val >= 0 && (num[i].changed ||
899 		    !num[i].seenagain)) {
900 			pr_number(num[i].infoname, (char *)0,
901 			    (char *)0, num[i].val);
902 		}
903 	}
904 
905 	pr_nfooting();
906 	pr_sheading();
907 
908 	/* Print out all strs that are different. */
909 	for (i = 0; i < numstrs; i++) {
910 		if (str[i].val == NULL && str[i].changed) {
911 			pr_string(str[i].infoname, (char *)0, (char *)0,
912 			    (char *)0);
913 		} else if ((str[i].val != NULL) &&
914 		    (str[i].changed || !str[i].seenagain)) {
915 			pr_string(str[i].infoname,
916 			    (char *)0, (char *)0, str[i].val);
917 		}
918 	}
919 
920 	pr_sfooting();
921 
922 	/* Finish it up. */
923 	for (i = firstoptind; i < argc; i++) {
924 		if (used[i - firstoptind]) {
925 			(void) printf("\tuse=%s,\n", argv[i]);
926 		} else {
927 			(void) fprintf(stderr,
928 			    "%s: 'use=%s' did not add anything to the "
929 			    "description.\n", progname, argv[i]);
930 		}
931 	}
932 }
933 
934 void
935 local_setenv(char *termNinfo)
936 {
937 	extern char **environ;
938 	static char *newenviron[2] = { 0, 0 };
939 	static unsigned int termsize = BUFSIZ;
940 	static char _terminfo[BUFSIZ];
941 	static char *terminfo = &_terminfo[0];
942 	register int termlen;
943 
944 	if (termNinfo && *termNinfo) {
945 		if (verbose) {
946 			(void) fprintf(trace, "setting TERMINFO=%s.\n",
947 			    termNinfo);
948 		}
949 
950 		termlen = strlen(termNinfo);
951 		if (termlen + 10 > termsize) {
952 			termsize = termlen + 20;
953 			terminfo = (char *) malloc(termsize * sizeof (char));
954 		}
955 		if (terminfo == (char *) NULL)
956 			badmalloc();
957 		(void) sprintf(terminfo, "TERMINFO=%s", termNinfo);
958 		newenviron[0] = terminfo;
959 	} else
960 		newenviron[0] = (char *) 0;
961 	environ = newenviron;
962 }
963 
964 int
965 main(int argc, char **argv)
966 {
967 	int i, c, firstoptind;
968 	char *tempargv[2];
969 	char *term = getenv("TERM");
970 
971 	term1info = term2info = getenv("TERMINFO");
972 	progname = argv[0];
973 
974 	/* parse options */
975 	while ((c = getopt(argc, argv, "ducnILCvV1rw:s:A:B:")) != EOF)
976 		switch (c) {
977 			case 'v':	verbose++;
978 					break;
979 			case '1':	pr_onecolumn(1);
980 					break;
981 			case 'w':	pr_width(atoi(optarg));
982 					break;
983 			case 'd':	diff++;
984 					break;
985 			case 'c':	common++;
986 					break;
987 			case 'n':	neither++;
988 					break;
989 			case 'u':	use++;
990 					break;
991 			case 'L':	pr_init(printing = pr_longnames);
992 					break;
993 			case 'I':	pr_init(printing = pr_terminfo);
994 					break;
995 			case 'C':	pr_init(printing = pr_cap);
996 					break;
997 			case 'A':	term1info = optarg; Aflag++;
998 					break;
999 			case 'B':	term2info = optarg; Bflag++;
1000 					break;
1001 			case 'r':	pr_caprestrict(0);
1002 					break;
1003 			case 's':
1004 				if (strcmp(optarg, "d") == 0)
1005 					sortorder = by_database;
1006 				else if (strcmp(optarg, "i") == 0)
1007 					sortorder = by_terminfo;
1008 				else if (strcmp(optarg, "l") == 0)
1009 					sortorder = by_longnames;
1010 				else if (strcmp(optarg, "c") == 0)
1011 					sortorder = by_cap;
1012 				else
1013 					goto usage;
1014 				break;
1015 			case 'V':
1016 				(void) printf("%s: version %s\n", progname,
1017 				    "@(#)curses:screen/infocmp.c	1.13");
1018 				exit(0);
1019 			case '?':
1020 				usage:
1021 				(void) fprintf(stderr,
1022 				    "usage: %s [-ducn] [-ILC] [-1Vv] "
1023 				    "[-s d|i|l|c] [-A directory] "
1024 				    "[-B directory] term-names ...\n",
1025 				    progname);
1026 				(void) fprintf(stderr, "\t-d\tprint "
1027 				    "differences (the default for >1 "
1028 				    "term-name)\n");
1029 				(void) fprintf(stderr, "\t-u\tproduce "
1030 				    "relative description\n");
1031 				(void) fprintf(stderr, "\t-c\tprint common "
1032 				    "entries\n");
1033 				(void) fprintf(stderr, "\t-n\tprint entries "
1034 				    "in neither\n");
1035 				(void) fprintf(stderr, "\t-I\tprint terminfo "
1036 				    "entries (the default for 1 term-name)\n");
1037 				(void) fprintf(stderr, "\t-C\tprint termcap "
1038 				    "entries\n");
1039 				(void) fprintf(stderr, "\t-L\tprint long C "
1040 				    "variable names\n");
1041 				(void) fprintf(stderr, "\t-1\tsingle column "
1042 				    "output\n");
1043 				(void) fprintf(stderr, "\t-V\tprint program "
1044 				    "version\n");
1045 				(void) fprintf(stderr, "\t-v\tverbose "
1046 				    "debugging output\n");
1047 				(void) fprintf(stderr, "\t-s\tchange sort "
1048 				    "order\n");
1049 				(void) fprintf(stderr, "\t-A\tset $TERMINFO "
1050 				    "for first term-name\n");
1051 				(void) fprintf(stderr, "\t-B\tset $TERMINFO "
1052 				    "for other term-names\n");
1053 				exit(-1);
1054 		}
1055 
1056 	argc -= optind;
1057 	argv += optind;
1058 	optind = 0;
1059 
1060 	/* Default to $TERM for -n, -I, -C and -L options. */
1061 	/* This is done by faking argv[][], argc and optind. */
1062 	if (neither && (argc == 0 || argc == 1)) {
1063 		if (argc == 0)
1064 			tempargv[0] = term;
1065 		else
1066 			tempargv[0] = argv[optind];
1067 		tempargv[1] = term;
1068 		argc = 2;
1069 		argv = tempargv;
1070 		optind = 0;
1071 	} else if ((printing != pr_none) && (argc == 0)) {
1072 		tempargv[0] = term;
1073 		argc = 1;
1074 		argv = tempargv;
1075 		optind = 0;
1076 	}
1077 
1078 	/* Check for enough names. */
1079 	if ((use || diff || common) && (argc <= 1)) {
1080 		(void) fprintf(stderr,
1081 		    "%s: must have at least two terminal names for a "
1082 		    "comparison to be done.\n", progname);
1083 		goto usage;
1084 	}
1085 
1086 	/* Set the default of diff -d or print -I */
1087 	if (!use && (printing == pr_none) && !common && !neither) {
1088 		if (argc == 0 || argc == 1) {
1089 			if (argc == 0) {
1090 				tempargv[0] = term;
1091 				argc = 1;
1092 				argv = tempargv;
1093 				optind = 0;
1094 			}
1095 			pr_init(printing = pr_terminfo);
1096 		} else {
1097 			diff++;
1098 		}
1099 	}
1100 
1101 	/* Set the default sorting order. */
1102 	if (sortorder == none) {
1103 		switch ((int) printing) {
1104 			case (int) pr_cap:
1105 				sortorder = by_cap; break;
1106 			case (int) pr_longnames:
1107 				sortorder = by_longnames; break;
1108 			case (int) pr_terminfo:
1109 			case (int) pr_none:
1110 				sortorder = by_terminfo; break;
1111 		}
1112 	}
1113 
1114 	firstterm = argv[optind++];
1115 	firstoptind = optind;
1116 
1117 	allocvariables(argc, firstoptind);
1118 	sortnames();
1119 
1120 	devnull = open("/dev/null", O_RDWR);
1121 	local_setenv(term1info);
1122 	initfirstterm(firstterm);
1123 	local_setenv(term2info);
1124 	for (i = 0; optind < argc; optind++, i++)
1125 		check_nth_terminal(argv[optind], i);
1126 
1127 	if (use)
1128 		dorelative(firstoptind, argc, argv);
1129 
1130 	return (0);
1131 }
1132