xref: /illumos-gate/usr/src/cmd/infocmp/infocmp.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
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 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.13	*/
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 	if ((ibool == NULL) || (num == NULL) || (str == NULL) ||
193 	    (use && (used == NULL)))
194 		badmalloc();
195 
196 	/* Fill in the names and initialize the structures. */
197 	nullseen = FALSE;
198 	for (i = 0; i < numbools; i++) {
199 		ibool[i].infoname = boolnames[i];
200 		ibool[i].capname = boolcodes[i];
201 		/* This is necessary until fnames.c is */
202 		/* incorporated into standard curses. */
203 		if (nullseen || (boolfnames[i] == NULL)) {
204 			ibool[i].fullname = "unknown_boolean";
205 			nullseen = TRUE;
206 		} else
207 			ibool[i].fullname = boolfnames[i];
208 		ibool[i].changed = FALSE;
209 		ibool[i].seenagain = FALSE;
210 	}
211 	nullseen = 0;
212 	for (i = 0; i < numnums; i++) {
213 		num[i].infoname = numnames[i];
214 		num[i].capname = numcodes[i];
215 		if (nullseen || (numfnames[i] == NULL)) {
216 			ibool[i].fullname = "unknown_number";
217 			nullseen = TRUE;
218 		} else
219 			num[i].fullname = numfnames[i];
220 		num[i].changed = FALSE;
221 		num[i].seenagain = FALSE;
222 	}
223 	nullseen = 0;
224 	for (i = 0; i < numstrs; i++) {
225 		str[i].infoname = strnames[i];
226 		str[i].capname = strcodes[i];
227 		if (nullseen || (strfnames[i] == NULL)) {
228 			str[i].fullname = "unknown_string";
229 			nullseen = TRUE;
230 		} else
231 			str[i].fullname = strfnames[i];
232 		str[i].changed = FALSE;
233 		str[i].seenagain = FALSE;
234 	}
235 }
236 
237 /*
238 	Routines to be passed to qsort(3) for comparison of the structures.
239 */
240 int
241 boolcompare(const void *x, const void *y)
242 {
243 	struct boolstruct *a;
244 	struct boolstruct *b;
245 
246 	a = (struct boolstruct *)x;
247 	b = (struct boolstruct *)y;
248 
249 	switch ((int) sortorder) {
250 		case (int) by_terminfo:
251 			return (strcmp(a->infoname, b->infoname));
252 		case (int) by_cap:
253 			return (strcmp(a->capname, b->capname));
254 		case (int) by_longnames:
255 			return (strcmp(a->fullname, b->fullname));
256 		default:
257 			return (0);
258 	}
259 }
260 
261 int
262 numcompare(const void *x, const void *y)
263 {
264 	struct numstruct *a;
265 	struct numstruct *b;
266 
267 	a = (struct numstruct *)x;
268 	b = (struct numstruct *)y;
269 	switch ((int) sortorder) {
270 		case (int) by_terminfo:
271 			return (strcmp(a->infoname, b->infoname));
272 		case (int) by_cap:
273 			return (strcmp(a->capname, b->capname));
274 		case (int) by_longnames:
275 			return (strcmp(a->fullname, b->fullname));
276 		default:
277 			return (0);
278 	}
279 }
280 
281 int
282 strcompare(const void *x, const void *y)
283 {
284 	struct strstruct *a;
285 	struct strstruct *b;
286 
287 	a = (struct strstruct *)x;
288 	b = (struct strstruct *)y;
289 
290 	switch ((int) sortorder) {
291 		case (int) by_terminfo:
292 			return (strcmp(a->infoname, b->infoname));
293 		case (int) by_cap:
294 			return (strcmp(a->capname, b->capname));
295 		case (int) by_longnames:
296 			return (strcmp(a->fullname, b->fullname));
297 		default:
298 			return (0);
299 	}
300 }
301 
302 /*
303 	Sort the entries by their terminfo name.
304 */
305 static void
306 sortnames()
307 {
308 	if (sortorder != by_database) {
309 		qsort((char *) ibool, (unsigned) numbools,
310 			sizeof (struct boolstruct), boolcompare);
311 		qsort((char *) num, (unsigned) numnums,
312 			sizeof (struct numstruct), numcompare);
313 		qsort((char *) str, (unsigned) numstrs,
314 			sizeof (struct strstruct), strcompare);
315 	}
316 	return;
317 }
318 
319 /*
320 	Print out a string, or "NULL" if it's not defined.
321 */
322 void
323 PR(FILE *stream, char *string)
324 {
325 	if (string == NULL)
326 		(void) fprintf(stream, "NULL");
327 	else
328 		tpr(stream, string);
329 }
330 
331 /*
332 	Output the 'ko' termcap string. This is a list of all of the input
333 	keys that input the same thing as the corresponding output strings.
334 */
335 int kncounter;
336 char kobuffer[512];
337 
338 char
339 *addko(char *output, char *input, char *koptr)
340 {
341 	char *inptr, *outptr, padbuffer[512];
342 	inptr = tgetstr(input, (char **)0);
343 	if (inptr == NULL)
344 		return (koptr);
345 	outptr = tgetstr(output, (char **)0);
346 	if (outptr == NULL)
347 		return (koptr);
348 	outptr = rmpadding(outptr, padbuffer, (int *) 0);
349 	if (strcmp(inptr, outptr) == 0) {
350 		*koptr++ = *output++;
351 		*koptr++ = *output++;
352 		*koptr++ = ',';
353 		kncounter++;
354 	}
355 	return (koptr);
356 }
357 
358 void
359 setupknko()
360 {
361 	char *koptr;
362 
363 	kncounter = 0;
364 	koptr = kobuffer;
365 
366 	koptr = addko("bs", "kb", koptr);	/* key_backspace */
367 	koptr = addko("bt", "kB", koptr);	/* key_btab */
368 	koptr = addko("cl", "kC", koptr);	/* key_clear */
369 	koptr = addko("le", "kl", koptr);	/* key_left */
370 	koptr = addko("do", "kd", koptr);	/* key_down */
371 	koptr = addko("nd", "kr", koptr);	/* key_right */
372 	koptr = addko("up", "ku", koptr);	/* key_up */
373 	koptr = addko("dc", "kD", koptr);	/* key_dc */
374 	koptr = addko("dl", "kL", koptr);	/* key_dl */
375 	koptr = addko("cd", "kS", koptr);	/* key_eos */
376 	koptr = addko("ce", "kE", koptr);	/* key_eol */
377 	koptr = addko("ho", "kh", koptr);	/* key_home */
378 	koptr = addko("st", "kT", koptr);	/* key_stab */
379 	koptr = addko("ic", "kI", koptr);	/* key_ic */
380 	koptr = addko("im", "kI", koptr);	/* key_ic */
381 	koptr = addko("al", "kA", koptr);	/* key_il */
382 	koptr = addko("sf", "kF", koptr);	/* key_sf */
383 	koptr = addko("ll", "kH", koptr);	/* key_ll */
384 	koptr = addko("sr", "kR", koptr);	/* key_sr */
385 	koptr = addko("ei", "kM", koptr);	/* key_eic */
386 	koptr = addko("ct", "ka", koptr);	/* key_catab */
387 
388 	/* get rid of comma */
389 	if (koptr != kobuffer)
390 		*(--koptr) = '\0';
391 }
392 
393 void
394 pr_kn()
395 {
396 	if (kncounter > 0)
397 		pr_number((char *)0, "kn", (char *)0, kncounter);
398 }
399 
400 void
401 pr_ko()
402 {
403 	if (kncounter > 0)
404 		pr_string((char *)0, "ko", (char *)0, kobuffer);
405 }
406 
407 void
408 pr_bcaps()
409 {
410 	char *retptr;
411 	char padbuffer[512];
412 
413 	if (verbose)
414 		(void) fprintf(trace, "looking at 'bs'\n");
415 	retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
416 	if (strcmp("\\b", retptr) == 0)
417 		pr_boolean((char *)0, "bs", (char *)0, 1);
418 
419 	if (verbose)
420 		(void) fprintf(trace, "looking at 'pt'\n");
421 	retptr = cconvert(rmpadding(tab, padbuffer, (int *) 0));
422 	if (strcmp("\\t", retptr) == 0)
423 		pr_boolean((char *)0, "pt", (char *)0, 1);
424 
425 	if (verbose)
426 		(void) fprintf(trace, "looking at 'nc'\n");
427 	retptr = cconvert(rmpadding(carriage_return, padbuffer, (int *) 0));
428 	if (strcmp("\\r", retptr) != 0)
429 		pr_boolean((char *)0, "nc", (char *)0, 1);
430 
431 	if (verbose)
432 		(void) fprintf(trace, "looking at 'ns'\n");
433 	if (scroll_forward == NULL)
434 		pr_boolean((char *)0, "ns", (char *)0, 1);
435 
436 	/* Ignore "xr": Return acts like ce \r \n (Delta Data) */
437 }
438 
439 void
440 pr_ncaps()
441 {
442 	char padbuffer[512];
443 	int padding;
444 
445 	if (verbose)
446 		(void) fprintf(trace, "looking at 'ug'\n");
447 	/* Duplicate sg for ug: Number of blank chars left by us or ue */
448 	if (magic_cookie_glitch > -1)
449 		pr_number((char *)0, "ug", (char *)0, magic_cookie_glitch);
450 
451 	if (verbose)
452 		(void) fprintf(trace, "looking at 'dB'\n");
453 	/* Number of millisec of bs delay needed */
454 	(void) rmpadding(cursor_left, padbuffer, &padding);
455 	if (padding > 0)
456 		pr_number((char *)0, "dB", (char *)0, padding);
457 
458 	if (verbose)
459 		(void) fprintf(trace, "looking at 'dC'\n");
460 	/* Number of millisec of cr delay needed */
461 	(void) rmpadding(carriage_return, padbuffer, &padding);
462 	if (padding > 0)
463 		pr_number((char *)0, "dC", (char *)0, padding);
464 
465 	if (verbose)
466 		(void) fprintf(trace, "looking at 'dF'\n");
467 	/* Number of millisec of ff delay needed */
468 	(void) rmpadding(form_feed, padbuffer, &padding);
469 	if (padding > 0)
470 		pr_number((char *)0, "dF", (char *)0, padding);
471 
472 	if (verbose)
473 		(void) fprintf(trace, "looking at 'dN'\n");
474 	/* Number of millisec of nl delay needed */
475 	(void) rmpadding(cursor_down, padbuffer, &padding);
476 	if (padding > 0)
477 		pr_number((char *)0, "dN", (char *)0, padding);
478 
479 	if (verbose)
480 		(void) fprintf(trace, "looking at 'dT'\n");
481 	/* Number of millisec of tab delay needed */
482 	(void) rmpadding(tab, padbuffer, &padding);
483 	if (padding > 0)
484 		pr_number((char *)0, "dT", (char *)0, padding);
485 
486 	/* Handle "kn": Number of "other" keys */
487 	setupknko();
488 	pr_kn();
489 }
490 
491 void
492 pr_scaps()
493 {
494 	char *retptr;
495 	char padbuffer[512];
496 
497 	/* Backspace if not "^H" */
498 	if (verbose)
499 		(void) fprintf(trace, "looking at 'bc'\n");
500 	retptr = cconvert(rmpadding(cursor_left, padbuffer, (int *) 0));
501 	if (strcmp("\\b", retptr) != 0)
502 		pr_string((char *)0, "bc", (char *)0, cursor_left);
503 
504 	/* Newline character (default "\n") */
505 	if (verbose)
506 		(void) fprintf(trace, "looking at 'nl'\n");
507 	retptr = cconvert(rmpadding(cursor_down, padbuffer, (int *) 0));
508 	if (strcmp("\\n", retptr) != 0)
509 		pr_string((char *)0, "nl", (char *)0, cursor_down);
510 
511 	/* Handle "ko" here: Termcap entries for other non-function keys */
512 	pr_ko();
513 
514 	/* Ignore "ma": Arrow key map, used by vi version 2 only */
515 }
516 
517 /*
518 	Set up the first terminal and save the values from it.
519 */
520 void
521 initfirstterm(char *term)
522 {
523 	register int i;
524 
525 	if (verbose)
526 		(void) fprintf(trace, "setting up terminal type '%s'.\n",
527 		    term);
528 
529 	(void) setupterm(term, devnull, (int *) 0);
530 
531 	/* Save the name for later use. */
532 	if (use) {
533 		register unsigned int length;
534 		savettytype = _savettytype;
535 		if ((length = strlen(ttytype)) >= TTYLEN) {
536 			savettytype = malloc(length);
537 			if (savettytype == NULL) {
538 				(void) fprintf(stderr, "%s: malloc is out "
539 				    "of space\n", progname);
540 				(void) strncpy(_savettytype, ttytype,
541 				    TTYLEN-1);
542 				_savettytype[TTYLEN] = '\0';
543 				savettytype = _savettytype;
544 			}
545 		} else
546 			(void) strcpy(_savettytype, ttytype);
547 	}
548 
549 	if (printing != pr_none) {
550 		pr_heading(term, ttytype);
551 		pr_bheading();
552 	}
553 
554 	/* Save the values for the first terminal. */
555 	for (i = 0; i < numbools; i++) {
556 		if ((ibool[i].val = tgetflag(ibool[i].capname)) &&
557 		    printing != pr_none)
558 			pr_boolean(ibool[i].infoname, ibool[i].capname,
559 			    ibool[i].fullname, 1);
560 		if (verbose)
561 			(void) fprintf(trace, "%s=%d.\n", ibool[i].infoname,
562 			    ibool[i].val);
563 	}
564 
565 	if (printing != pr_none) {
566 		if (printing == pr_cap)
567 			pr_bcaps();
568 		pr_bfooting();
569 		pr_nheading();
570 	}
571 
572 	for (i = 0; i < numnums; i++) {
573 		if (((num[i].val = tgetnum(num[i].capname)) > -1) &&
574 		    printing != pr_none)
575 			pr_number(num[i].infoname, num[i].capname,
576 			    num[i].fullname, num[i].val);
577 		if (verbose)
578 			(void) fprintf(trace, "%s=%d.\n", num[i].infoname,
579 			    num[i].val);
580 	}
581 
582 	if (printing != pr_none) {
583 		if (printing == pr_cap)
584 			pr_ncaps();
585 		pr_nfooting();
586 		pr_sheading();
587 	}
588 
589 	for (i = 0; i < numstrs; i++) {
590 		str[i].val = tgetstr(str[i].capname, (char **)0);
591 		if ((str[i].val != NULL) && printing != pr_none)
592 			pr_string(str[i].infoname, str[i].capname,
593 			    str[i].fullname, str[i].val);
594 		if (verbose) {
595 			(void) fprintf(trace, "%s='", str[i].infoname);
596 			PR(trace, str[i].val);
597 			(void) fprintf(trace, "'.\n");
598 		}
599 	}
600 
601 	if (printing == pr_cap)
602 		pr_scaps();
603 
604 	if (printing != pr_none)
605 		pr_sfooting();
606 }
607 
608 /*
609 	Set up the n'th terminal.
610 */
611 static void
612 check_nth_terminal(char *nterm, int n)
613 {
614 	register char boolval;
615 	register short numval;
616 	register char *strval;
617 	register int i;
618 
619 	if (use)
620 		used[n] = FALSE;
621 
622 	if (verbose)
623 		(void) fprintf(trace, "adding in terminal type '%s'.\n",
624 		    nterm);
625 
626 	(void) setupterm(nterm, devnull, (int *) 0);
627 
628 	if (printing != pr_none) {
629 		pr_heading(nterm, ttytype);
630 		pr_bheading();
631 	}
632 
633 	if (diff || common || neither) {
634 		if (Aflag && Bflag)
635 			(void) printf("comparing %s (TERMINFO=%s) to %s "
636 			    "(TERMINFO=%s).\n",
637 			firstterm, term1info, nterm, term2info);
638 		else if (Aflag)
639 			(void) printf("comparing %s (TERMINFO=%s) to %s.\n",
640 			    firstterm, term1info, nterm);
641 		else if (Bflag)
642 			(void) printf("comparing %s to %s (TERMINFO=%s).\n",
643 			    firstterm, nterm, term2info);
644 		else
645 			(void) printf("comparing %s to %s.\n",
646 			    firstterm, nterm);
647 		(void) printf("    comparing booleans.\n");
648 	}
649 
650 	/* save away the values for the nth terminal */
651 	for (i = 0; i < numbools; i++) {
652 		boolval = tgetflag(ibool[i].capname);
653 		if (use) {
654 			if (ibool[i].seenagain) {
655 			/*
656 			** We do not have to worry about this impossible case
657 			** since booleans can have only two values: true and
658 			** false.
659 			** if (boolval && (boolval != ibool[i].secondval))
660 			**  {
661 			**  (void) fprintf(trace, "use= order dependency"
662 			**  "found:\n");
663 			**  (void) fprintf(trace, "    %s: %s has %d, %s has"
664 			**   " %d.\n",
665 			**	ibool[i].capname, ibool[i].secondname,
666 			**	ibool[i].secondval, nterm, boolval);
667 			**  }
668 			*/
669 			} else {
670 				if (boolval == TRUE) {
671 					ibool[i].seenagain = TRUE;
672 					ibool[i].secondval = boolval;
673 					ibool[i].secondname = nterm;
674 					if (ibool[i].val != boolval)
675 						ibool[i].changed = TRUE;
676 					else
677 						used[n] = TRUE;
678 				}
679 			}
680 		}
681 		if (boolval) {
682 			if (printing != pr_none)
683 				pr_boolean(ibool[i].infoname, ibool[i].capname,
684 				    ibool[i].fullname, 1);
685 			if (common && (ibool[i].val == boolval))
686 				(void) printf("\t%s= T.\n", ibool[i].infoname);
687 		} else if (neither && !ibool[i].val)
688 			(void) printf("\t!%s.\n", ibool[i].infoname);
689 		if (diff && (ibool[i].val != boolval))
690 			(void) printf("\t%s: %c:%c.\n", ibool[i].infoname,
691 			    ibool[i].val?'T':'F', boolval?'T':'F');
692 		if (verbose)
693 			(void) fprintf(trace, "%s: %d:%d, changed=%d, "
694 			    "seen=%d.\n", ibool[i].infoname, ibool[i].val,
695 			    boolval, ibool[i].changed, ibool[i].seenagain);
696 	}
697 
698 	if (printing != pr_none) {
699 		if (printing == pr_cap)
700 			pr_bcaps();
701 		pr_bfooting();
702 		pr_nheading();
703 	}
704 
705 	if (diff || common || neither)
706 		(void) printf("    comparing numbers.\n");
707 
708 	for (i = 0; i < numnums; i++) {
709 		numval = tgetnum(num[i].capname);
710 		if (use) {
711 			if (num[i].seenagain) {
712 				if ((numval > -1) &&
713 				    (numval != num[i].secondval)) {
714 					(void) fprintf(stderr,
715 					    "%s: use = order dependency "
716 					    "found:\n", progname);
717 					(void) fprintf(stderr, "    %s: %s "
718 					    "has %d, %s has %d.\n",
719 					    num[i].capname, num[i].secondname,
720 					    num[i].secondval, nterm, numval);
721 				}
722 			} else {
723 				if (numval > -1) {
724 					num[i].seenagain = TRUE;
725 					num[i].secondval = numval;
726 					num[i].secondname = nterm;
727 					if ((numval > -1) &&
728 					    (num[i].val != numval))
729 						num[i].changed = TRUE;
730 					else
731 						used[n] = TRUE;
732 				}
733 			}
734 		}
735 		if (numval > -1) {
736 			if (printing != pr_none)
737 				pr_number(num[i].infoname, num[i].capname,
738 				    num[i].fullname, numval);
739 			if (common && (num[i].val == numval))
740 				(void) printf("\t%s= %d.\n", num[i].infoname,
741 				    numval);
742 			} else if (neither && (num[i].val == -1))
743 				(void) printf("\t!%s.\n", num[i].infoname);
744 			if (diff && (num[i].val != numval))
745 				(void) printf("\t%s: %d:%d.\n",
746 				    num[i].infoname, num[i].val, numval);
747 			if (verbose)
748 				(void) fprintf(trace, "%s: %d:%d, "
749 				    "changed = %d, seen = %d.\n",
750 				    num[i].infoname, num[i].val, numval,
751 				    num[i].changed, num[i].seenagain);
752 	}
753 
754 	if (printing != pr_none) {
755 		if (printing == pr_cap)
756 			pr_ncaps();
757 		pr_nfooting();
758 		pr_sheading();
759 	}
760 
761 	if (diff || common || neither)
762 		(void) printf("    comparing strings.\n");
763 
764 	for (i = 0; i < numstrs; i++) {
765 		strval = tgetstr(str[i].capname, (char **)0);
766 		if (use) {
767 			if (str[i].seenagain && (strval != NULL)) {
768 				if (!EQUAL(strval, str[i].secondval)) {
769 					(void) fprintf(stderr,
770 					    "use= order dependency"
771 					    "  found:\n");
772 					(void) fprintf(stderr,
773 					    "    %s: %s has '",
774 					    str[i].capname, str[i].secondname);
775 					PR(stderr, str[i].secondval);
776 					(void) fprintf(stderr,
777 					    "', %s has '", nterm);
778 					PR(stderr, strval);
779 					(void) fprintf(stderr, "'.\n");
780 				}
781 			} else {
782 				if (strval != NULL) {
783 					str[i].seenagain = TRUE;
784 					str[i].secondval = strval;
785 					str[i].secondname = nterm;
786 					if (!EQUAL(str[i].val, strval))
787 						str[i].changed = TRUE;
788 					else
789 						used[n] = TRUE;
790 				}
791 			}
792 		}
793 		if (strval != NULL) {
794 			if (printing != pr_none)
795 				pr_string(str[i].infoname, str[i].capname,
796 				    str[i].fullname, strval);
797 			if (common && EQUAL(str[i].val, strval)) {
798 				(void) printf("\t%s= '", str[i].infoname);
799 				PR(stdout, strval);
800 				(void) printf("'.\n");
801 			}
802 		} else if (neither && (str[i].val == NULL))
803 			(void) printf("\t!%s.\n", str[i].infoname);
804 		if (diff && !EQUAL(str[i].val, strval)) {
805 			(void) printf("\t%s: '", str[i].infoname);
806 			PR(stdout, str[i].val);
807 			(void) printf("','");
808 			PR(stdout, strval);
809 			(void) printf("'.\n");
810 		}
811 		if (verbose) {
812 			(void) fprintf(trace, "%s: '", str[i].infoname);
813 			PR(trace, str[i].val);
814 			(void) fprintf(trace, "':'");
815 			PR(trace, strval);
816 			(void) fprintf(trace, "',changed=%d,seen=%d.\n",
817 			    str[i].changed, str[i].seenagain);
818 		}
819 	}
820 
821 	if (printing == pr_cap)
822 		pr_scaps();
823 
824 	if (printing != pr_none)
825 		pr_sfooting();
826 
827 	return;
828 }
829 
830 /*
831 	A capability gets an at-sign if it no longer exists, but
832 	one of the relative entries contains a value for it.
833 	It gets printed if the original value is not seen in ANY
834 	of the relative entries, or if the FIRST relative entry that has
835 	the capability gives a DIFFERENT value for the capability.
836 */
837 void
838 dorelative(int firstoptind, int argc, char **argv)
839 {
840 	register int i;
841 
842 	/* turn off printing of termcap and long names */
843 	pr_init(pr_terminfo);
844 
845 	/* print out the entry name */
846 	pr_heading((char *)0, savettytype);
847 
848 	pr_bheading();
849 
850 	/* Print out all bools that are different. */
851 	for (i = 0; i < numbools; i++)
852 		if (!ibool[i].val && ibool[i].changed)
853 			pr_boolean(ibool[i].infoname, (char *)0,
854 			    (char *)0, -1);
855 		else if (ibool[i].val && (ibool[i].changed ||
856 		    !ibool[i].seenagain))
857 			pr_boolean(ibool[i].infoname, (char *)0, (char *)0, 1);
858 
859 	pr_bfooting();
860 	pr_nheading();
861 
862 	/* Print out all nums that are different. */
863 	for (i = 0; i < numnums; i++)
864 		if (num[i].val < 0 && num[i].changed)
865 			pr_number(num[i].infoname, (char *)0, (char *)0, -1);
866 		else if (num[i].val >= 0 && (num[i].changed ||
867 		    !num[i].seenagain))
868 			pr_number(num[i].infoname, (char *)0,
869 			    (char *)0, num[i].val);
870 
871 	pr_nfooting();
872 	pr_sheading();
873 
874 	/* Print out all strs that are different. */
875 	for (i = 0; i < numstrs; i++)
876 		if (str[i].val == NULL && str[i].changed)
877 			pr_string(str[i].infoname, (char *)0, (char *)0,
878 			    (char *)0);
879 		else if ((str[i].val != NULL) &&
880 		    (str[i].changed || !str[i].seenagain))
881 	pr_string(str[i].infoname, (char *)0, (char *)0, str[i].val);
882 
883 	pr_sfooting();
884 
885 	/* Finish it up. */
886 	for (i = firstoptind; i < argc; i++)
887 		if (used[i - firstoptind])
888 			(void) printf("\tuse=%s,\n", argv[i]);
889 		else
890 			(void) fprintf(stderr,
891 			    "%s: 'use=%s' did not add anything to the "
892 			    "description.\n", progname, argv[i]);
893 }
894 
895 void
896 local_setenv(char *termNinfo)
897 {
898 	extern char **environ;
899 	static char *newenviron[2] = { 0, 0 };
900 	static unsigned int termsize = BUFSIZ;
901 	static char _terminfo[BUFSIZ];
902 	static char *terminfo = &_terminfo[0];
903 	register int termlen;
904 
905 	if (termNinfo && *termNinfo) {
906 		if (verbose)
907 			(void) fprintf(trace, "setting TERMINFO=%s.\n",
908 			    termNinfo);
909 		termlen = strlen(termNinfo);
910 		if (termlen + 10 > termsize) {
911 			termsize = termlen + 20;
912 			terminfo = (char *) malloc(termsize * sizeof (char));
913 		}
914 		if (terminfo == (char *) NULL)
915 			badmalloc();
916 		(void) sprintf(terminfo, "TERMINFO=%s", termNinfo);
917 		newenviron[0] = terminfo;
918 	} else
919 		newenviron[0] = (char *) 0;
920 	environ = newenviron;
921 }
922 
923 int
924 main(int argc, char **argv)
925 {
926 	int i, c, firstoptind;
927 	char *tempargv[2];
928 	char *term = getenv("TERM");
929 
930 	term1info = term2info = getenv("TERMINFO");
931 	progname = argv[0];
932 
933 	/* parse options */
934 	while ((c = getopt(argc, argv, "ducnILCvV1rw:s:A:B:")) != EOF)
935 		switch (c) {
936 			case 'v':	verbose++;
937 					break;
938 			case '1':	pr_onecolumn(1);
939 					break;
940 			case 'w':	pr_width(atoi(optarg));
941 					break;
942 			case 'd':	diff++;
943 					break;
944 			case 'c':	common++;
945 					break;
946 			case 'n':	neither++;
947 					break;
948 			case 'u':	use++;
949 					break;
950 			case 'L':	pr_init(printing = pr_longnames);
951 					break;
952 			case 'I':	pr_init(printing = pr_terminfo);
953 					break;
954 			case 'C':	pr_init(printing = pr_cap);
955 					break;
956 			case 'A':	term1info = optarg; Aflag++;
957 					break;
958 			case 'B':	term2info = optarg; Bflag++;
959 					break;
960 			case 'r':	pr_caprestrict(0);
961 					break;
962 			case 's':
963 				if (strcmp(optarg, "d") == 0)
964 					sortorder = by_database;
965 				else if (strcmp(optarg, "i") == 0)
966 					sortorder = by_terminfo;
967 				else if (strcmp(optarg, "l") == 0)
968 					sortorder = by_longnames;
969 				else if (strcmp(optarg, "c") == 0)
970 					sortorder = by_cap;
971 				else
972 					goto usage;
973 				break;
974 			case 'V':
975 				(void) printf("%s: version %s\n", progname,
976 				    "@(#)curses:screen/infocmp.c	1.13");
977 				exit(0);
978 			case '?':
979 				usage:
980 				(void) fprintf(stderr,
981 				    "usage: %s [-ducn] [-ILC] [-1Vv] "
982 				    "[-s d|i|l|c] [-A directory] "
983 				    "[-B directory] term-names ...\n",
984 				    progname);
985 				(void) fprintf(stderr, "\t-d\tprint "
986 				    "differences (the default for >1 "
987 				    "term-name)\n");
988 				(void) fprintf(stderr, "\t-u\tproduce "
989 				    "relative description\n");
990 				(void) fprintf(stderr, "\t-c\tprint common "
991 				    "entries\n");
992 				(void) fprintf(stderr, "\t-n\tprint entries "
993 				    "in neither\n");
994 				(void) fprintf(stderr, "\t-I\tprint terminfo "
995 				    "entries (the default for 1 term-name)\n");
996 				(void) fprintf(stderr, "\t-C\tprint termcap "
997 				    "entries\n");
998 				(void) fprintf(stderr, "\t-L\tprint long C "
999 				    "variable names\n");
1000 				(void) fprintf(stderr, "\t-1\tsingle column "
1001 				    "output\n");
1002 				(void) fprintf(stderr, "\t-V\tprint program "
1003 				    "version\n");
1004 				(void) fprintf(stderr, "\t-v\tverbose "
1005 				    "debugging output\n");
1006 				(void) fprintf(stderr, "\t-s\tchange sort "
1007 				    "order\n");
1008 				(void) fprintf(stderr, "\t-A\tset $TERMINFO "
1009 				    "for first term-name\n");
1010 				(void) fprintf(stderr, "\t-B\tset $TERMINFO "
1011 				    "for other term-names\n");
1012 				exit(-1);
1013 		}
1014 
1015 	argc -= optind;
1016 	argv += optind;
1017 	optind = 0;
1018 
1019 	/* Default to $TERM for -n, -I, -C and -L options. */
1020 	/* This is done by faking argv[][], argc and optind. */
1021 	if (neither && (argc == 0 || argc == 1)) {
1022 		if (argc == 0)
1023 			tempargv[0] = term;
1024 		else
1025 			tempargv[0] = argv[optind];
1026 		tempargv[1] = term;
1027 		argc = 2;
1028 		argv = tempargv;
1029 		optind = 0;
1030 	} else if ((printing != pr_none) && (argc == 0)) {
1031 		tempargv[0] = term;
1032 		argc = 1;
1033 		argv = tempargv;
1034 		optind = 0;
1035 	}
1036 
1037 	/* Check for enough names. */
1038 	if ((use || diff || common) && (argc <= 1)) {
1039 		(void) fprintf(stderr,
1040 		    "%s: must have at least two terminal names for a "
1041 		    "comparison to be done.\n", progname);
1042 		goto usage;
1043 	}
1044 
1045 	/* Set the default of diff -d or print -I */
1046 	if (!use && (printing == pr_none) && !common && !neither) {
1047 		if (argc == 0 || argc == 1) {
1048 			if (argc == 0) {
1049 				tempargv[0] = term;
1050 				argc = 1;
1051 				argv = tempargv;
1052 				optind = 0;
1053 			}
1054 			pr_init(printing = pr_terminfo);
1055 		} else
1056 			diff++;
1057 	}
1058 
1059 	/* Set the default sorting order. */
1060 	if (sortorder == none)
1061 		switch ((int) printing) {
1062 			case (int) pr_cap:
1063 				sortorder = by_cap; break;
1064 			case (int) pr_longnames:
1065 				sortorder = by_longnames; break;
1066 			case (int) pr_terminfo:
1067 			case (int) pr_none:
1068 				sortorder = by_terminfo; break;
1069 		}
1070 
1071 	firstterm = argv[optind++];
1072 	firstoptind = optind;
1073 
1074 	allocvariables(argc, firstoptind);
1075 	sortnames();
1076 
1077 	devnull = open("/dev/null", O_RDWR);
1078 	local_setenv(term1info);
1079 	initfirstterm(firstterm);
1080 	local_setenv(term2info);
1081 	for (i = 0; optind < argc; optind++, i++)
1082 		check_nth_terminal(argv[optind], i);
1083 
1084 	if (use)
1085 		dorelative(firstoptind, argc, argv);
1086 
1087 	return (0);
1088 }
1089