xref: /src/contrib/ncurses/progs/infocmp.c (revision 68ad2b0d7af2a3571c4abac9afa712f9b09b721c)
1 /****************************************************************************
2  * Copyright 2020-2024,2025 Thomas E. Dickey                                *
3  * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
32  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
33  *     and: Thomas E. Dickey                        1996-on                 *
34  ****************************************************************************/
35 
36 /*
37  *	infocmp.c -- decompile an entry, or compare two entries
38  *		written by Eric S. Raymond
39  *		and Thomas E Dickey
40  */
41 
42 #include <progs.priv.h>
43 
44 #include <dump_entry.h>
45 
46 MODULE_ID("$Id: infocmp.c,v 1.177 2025/12/27 22:18:10 tom Exp $")
47 
48 #ifndef ACTUAL_TIC
49 #define ACTUAL_TIC "tic"
50 #endif
51 
52 #define MAX_STRING	1024	/* maximum formatted string */
53 
54 const char *_nc_progname = "infocmp";
55 
56 typedef char path[PATH_MAX];
57 
58 /***************************************************************************
59  *
60  * The following control variables, together with the contents of the
61  * terminfo entries, completely determine the actions of the program.
62  *
63  ***************************************************************************/
64 
65 static ENTRY *entries;		/* terminfo entries */
66 static int termcount;		/* count of terminal entries */
67 
68 static const char usage_string[] = \
69 "Usage: %s [options] [-A directory] [-B directory] [termname...]";
70 
71 static bool limited = TRUE;	/* "-r" option is not set */
72 static bool quiet = FALSE;
73 static bool literal = FALSE;
74 static const char *bool_sep = ":";
75 static const char *s_absent = "NULL";
76 static const char *s_cancel = "NULL";
77 static const char *tversion;	/* terminfo version selected */
78 static unsigned itrace;		/* trace flag for debugging */
79 static int mwidth = 60;
80 static int mheight = 65535;
81 static int numbers = 0;		/* format "%'char'" to/from "%{number}" */
82 static int outform = F_TERMINFO;	/* output format */
83 static int sortmode;		/* sort_mode */
84 
85 /* main comparison mode */
86 static int compare;
87 #define C_DEFAULT	0	/* don't force comparison mode */
88 #define C_DIFFERENCE	1	/* list differences between two terminals */
89 #define C_COMMON	2	/* list common capabilities */
90 #define C_NAND		3	/* list capabilities in neither terminal */
91 #define C_USEALL	4	/* generate relative use-form entry */
92 static bool ignorepads;		/* ignore pad prefixes when diffing */
93 
94 #if NO_LEAKS
95 
96 typedef struct {
97     ENTRY *head;
98     ENTRY *tail;
99 } ENTERED;
100 
101 static ENTERED *entered;
102 
103 #undef ExitProgram
104 static GCC_NORETURN void ExitProgram(int code);
105 /* prototype is to get gcc to accept the noreturn attribute */
106 static void
ExitProgram(int code)107 ExitProgram(int code)
108 {
109     int n;
110 
111     for (n = 0; n < termcount; ++n) {
112 	ENTRY *new_head = _nc_head;
113 	ENTRY *new_tail = _nc_tail;
114 	_nc_head = entered[n].head;
115 	_nc_tail = entered[n].tail;
116 	_nc_free_entries(entered[n].head);
117 	_nc_head = new_head;
118 	_nc_tail = new_tail;
119     }
120     _nc_leaks_dump_entry();
121     free(entries);
122     free(entered);
123     _nc_free_tic(code);
124 }
125 #endif
126 
127 static void
failed(const char * s)128 failed(const char *s)
129 {
130     perror(s);
131     ExitProgram(EXIT_FAILURE);
132 }
133 
134 static void
canonical_name(const char * source,char * target)135 canonical_name(const char *source, char *target)
136 /* extract the terminal type's primary name */
137 {
138     int limit = NAMESIZE;
139 
140     while (--limit > 0) {
141 	char ch = *source++;
142 	if (ch == '|')
143 	    break;
144 	*target++ = ch;
145     }
146     *target = '\0';
147 }
148 
149 static bool
no_boolean(int value)150 no_boolean(int value)
151 {
152     bool result = (value == ABSENT_BOOLEAN);
153     if (!strcmp(s_absent, s_cancel))
154 	result = !VALID_BOOLEAN(value);
155     return result;
156 }
157 
158 static bool
no_numeric(int value)159 no_numeric(int value)
160 {
161     bool result = (value == ABSENT_NUMERIC);
162     if (!strcmp(s_absent, s_cancel))
163 	result = !VALID_NUMERIC(value);
164     return result;
165 }
166 
167 static bool
no_string(const char * const value)168 no_string(const char *const value)
169 {
170     bool result = (value == ABSENT_STRING);
171     if (!strcmp(s_absent, s_cancel))
172 	result = !VALID_STRING(value);
173     return result;
174 }
175 
176 /***************************************************************************
177  *
178  * Predicates for dump function
179  *
180  ***************************************************************************/
181 
182 static int
capcmp(PredIdx idx,const char * s,const char * t)183 capcmp(PredIdx idx, const char *s, const char *t)
184 /* capability comparison function */
185 {
186     if (!VALID_STRING(s) && !VALID_STRING(t))
187 	return (s != t);
188     else if (!VALID_STRING(s) || !VALID_STRING(t))
189 	return (1);
190 
191     if ((idx == acs_chars_index) || !ignorepads)
192 	return (strcmp(s, t));
193     else
194 	return (_nc_capcmp(s, t));
195 }
196 
197 /*
198  * Predicate function to use for "use=" decompilation.
199  *
200  * Return value is used in fmt_entry:
201  *   FAIL  show nothing for this capability.
202  *   FALSE show cancel for booleans (a compromise)
203  *   TRUE  show capability
204  *
205  * The only difference between FALSE/TRUE returns is in the treatment of
206  * booleans.
207  */
208 static int
use_predicate(unsigned type,PredIdx idx)209 use_predicate(unsigned type, PredIdx idx)
210 {
211     int result = FAIL;
212     ENTRY *ep;
213 
214     switch (type) {
215     case BOOLEAN:
216 	{
217 	    /*
218 	     * This assumes that multiple use entries are supposed
219 	     * to contribute the logical or of their boolean capabilities.
220 	     * This is true if we take the semantics of multiple uses to
221 	     * be 'each capability gets the first non-default value found
222 	     * in the sequence of use entries'.
223 	     *
224 	     * Note that cancelled or absent booleans are stored as FALSE,
225 	     * unlike numbers and strings, whose cancelled/absent state is
226 	     * recorded in the terminfo database.
227 	     */
228 	    if (idx < NUM_BOOLEANS(&(entries[0].tterm))) {
229 		int is_set = FALSE;
230 
231 		for (ep = &entries[1]; ep < entries + termcount; ep++) {
232 		    if (idx < NUM_BOOLEANS(&(ep->tterm))
233 			&& (is_set = ep->tterm.Booleans[idx])) {
234 			break;
235 		    }
236 		}
237 		if (is_set != entries[0].tterm.Booleans[idx])
238 		    result = (!is_set);
239 	    }
240 	}
241 	break;
242 
243     case NUMBER:
244 	{
245 	    /*
246 	     * We take the semantics of multiple uses to be 'each
247 	     * capability gets the first non-default value found
248 	     * in the sequence of use entries'.
249 	     */
250 	    if (idx < NUM_NUMBERS(&(entries[0].tterm))) {
251 		int value = ABSENT_NUMERIC;
252 
253 		for (ep = &entries[1]; ep < entries + termcount; ep++)
254 		    if (idx < NUM_NUMBERS(&(ep->tterm))
255 			&& VALID_NUMERIC(ep->tterm.Numbers[idx])) {
256 			value = ep->tterm.Numbers[idx];
257 			break;
258 		    }
259 
260 		if (value != entries[0].tterm.Numbers[idx])
261 		    result = (value != ABSENT_NUMERIC);
262 	    }
263 	}
264 	break;
265 
266     case STRING:
267 	{
268 	    const char *termstr = entries[0].tterm.Strings[idx];
269 	    const char *usestr = ABSENT_STRING;
270 
271 	    /*
272 	     * We take the semantics of multiple uses to be 'each
273 	     * capability gets the first non-default value found
274 	     * in the sequence of use entries'.
275 	     */
276 	    if (idx < NUM_STRINGS(&(entries[0].tterm))) {
277 		for (ep = &entries[1]; ep < entries + termcount; ep++)
278 		    if (idx < NUM_STRINGS(&(ep->tterm))
279 			&& ep->tterm.Strings[idx]) {
280 			usestr = ep->tterm.Strings[idx];
281 			break;
282 		    }
283 
284 		if (usestr == CANCELLED_STRING && termstr == ABSENT_STRING)
285 		    result = (FAIL);
286 		else if (usestr == CANCELLED_STRING && termstr == CANCELLED_STRING)
287 		    result = (TRUE);
288 		else if (usestr == ABSENT_STRING && termstr == ABSENT_STRING)
289 		    result = (FAIL);
290 		else if (!usestr || !termstr || capcmp(idx, usestr, termstr))
291 		    result = (TRUE);
292 	    }
293 	}
294 	break;
295 
296     default:
297 	result = FALSE;
298 	break;
299     }
300 
301     return (result);
302 }
303 
304 static bool
useeq(const ENTRY * e1,const ENTRY * e2)305 useeq(const ENTRY * e1, const ENTRY * e2)
306 /* are the use references in two entries equivalent? */
307 {
308     unsigned i, j;
309 
310     if (e1->nuses != e2->nuses)
311 	return (FALSE);
312 
313     /* Ugh...this is quadratic again */
314     for (i = 0; i < e1->nuses; i++) {
315 	bool foundmatch = FALSE;
316 
317 	/* search second entry for given use reference */
318 	for (j = 0; j < e2->nuses; j++)
319 	    if (!strcmp(e1->uses[i].name, e2->uses[j].name)) {
320 		foundmatch = TRUE;
321 		break;
322 	    }
323 
324 	if (!foundmatch)
325 	    return (FALSE);
326     }
327 
328     return (TRUE);
329 }
330 
331 static bool
entryeq(const TERMTYPE2 * t1,const TERMTYPE2 * t2)332 entryeq(const TERMTYPE2 *t1, const TERMTYPE2 *t2)
333 /* are two entries equivalent? */
334 {
335     unsigned i;
336 
337     for (i = 0; i < NUM_BOOLEANS(t1); i++)
338 	if (t1->Booleans[i] != t2->Booleans[i])
339 	    return (FALSE);
340 
341     for (i = 0; i < NUM_NUMBERS(t1); i++)
342 	if (t1->Numbers[i] != t2->Numbers[i])
343 	    return (FALSE);
344 
345     for (i = 0; i < NUM_STRINGS(t1); i++)
346 	if (capcmp((PredIdx) i, t1->Strings[i], t2->Strings[i]))
347 	    return (FALSE);
348 
349     return (TRUE);
350 }
351 
352 #define TIC_EXPAND(result) _nc_tic_expand(result, outform==F_TERMINFO, numbers)
353 
354 static void
print_uses(const ENTRY * ep,FILE * fp)355 print_uses(const ENTRY * ep, FILE *fp)
356 /* print an entry's use references */
357 {
358     if (!ep->nuses) {
359 	fputs("NULL", fp);
360     } else {
361 	unsigned i;
362 
363 	for (i = 0; i < ep->nuses; i++) {
364 	    fputs(ep->uses[i].name, fp);
365 	    if (i < ep->nuses - 1)
366 		fputs(" ", fp);
367 	}
368     }
369 }
370 
371 static const char *
dump_boolean(int val)372 dump_boolean(int val)
373 /* display the value of a boolean capability */
374 {
375     switch (val) {
376     case ABSENT_BOOLEAN:
377 	return (s_absent);
378     case CANCELLED_BOOLEAN:
379 	return (s_cancel);
380     case FALSE:
381 	return ("F");
382     case TRUE:
383 	return ("T");
384     default:
385 	return ("?");
386     }
387 }
388 
389 static void
dump_numeric(int val,char * buf)390 dump_numeric(int val, char *buf)
391 /* display the value of a numeric capability */
392 {
393     switch (val) {
394     case ABSENT_NUMERIC:
395 	_nc_STRCPY(buf, s_absent, MAX_STRING);
396 	break;
397     case CANCELLED_NUMERIC:
398 	_nc_STRCPY(buf, s_cancel, MAX_STRING);
399 	break;
400     default:
401 	_nc_SPRINTF(buf, _nc_SLIMIT(MAX_STRING) "%d", val);
402 	break;
403     }
404 }
405 
406 static void
dump_string(const char * val,char * buf)407 dump_string(const char *val, char *buf)
408 /* display the value of a string capability */
409 {
410     if (val == ABSENT_STRING)
411 	_nc_STRCPY(buf, s_absent, MAX_STRING);
412     else if (val == CANCELLED_STRING)
413 	_nc_STRCPY(buf, s_cancel, MAX_STRING);
414     else {
415 	_nc_SPRINTF(buf, _nc_SLIMIT(MAX_STRING)
416 		    "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val));
417     }
418 }
419 
420 /*
421  * Show "comparing..." message for the given terminal names.
422  */
423 static void
show_comparing(char ** names)424 show_comparing(char **names)
425 {
426     if (itrace) {
427 	switch (compare) {
428 	case C_DIFFERENCE:
429 	    (void) fprintf(stderr, "%s: dumping differences\n", _nc_progname);
430 	    break;
431 
432 	case C_COMMON:
433 	    (void) fprintf(stderr, "%s: dumping common capabilities\n", _nc_progname);
434 	    break;
435 
436 	case C_NAND:
437 	    (void) fprintf(stderr, "%s: dumping differences\n", _nc_progname);
438 	    break;
439 	}
440     }
441     if (*names) {
442 	printf("comparing %s", *names++);
443 	if (*names) {
444 	    printf(" to %s", *names++);
445 	    while (*names) {
446 		printf(", %s", *names++);
447 	    }
448 	}
449 	printf(".\n");
450     }
451 }
452 
453 /*
454  * ncurses stores two types of non-standard capabilities:
455  * a) capabilities listed past the "STOP-HERE" comment in the Caps file.
456  *    These are used in the terminfo source file to provide data for termcaps,
457  *    e.g., when there is no equivalent capability in terminfo, as well as for
458  *    widely-used non-standard capabilities.
459  * b) user-definable capabilities, via "tic -x".
460  *
461  * However, if "-x" is omitted from the tic command, both types of
462  * non-standard capability are not loaded into the terminfo database.  This
463  * macro is used for limit-checks against the symbols that tic uses to omit
464  * the two types of non-standard entry.
465  */
466 #if NCURSES_XNAMES
467 #define check_user_definable(n,limit) if (!_nc_user_definable && (n) > (limit)) break
468 #else
469 #define check_user_definable(n,limit) if ((n) > (limit)) break
470 #endif
471 
472 /*
473  * Use these macros to simplify loops on C_COMMON and C_NAND:
474  */
475 #define for_each_entry() while (entries[extra].tterm.term_names)
476 #define next_entry           (&(entries[extra++].tterm))
477 
478 static void
compare_predicate(PredType type,PredIdx idx,const char * name)479 compare_predicate(PredType type, PredIdx idx, const char *name)
480 /* predicate function to use for entry difference reports */
481 {
482     const ENTRY *e1 = &entries[0];
483     const ENTRY *e2 = &entries[1];
484     char buf1[MAX_STRING];
485     char buf2[MAX_STRING];
486     int b1, b2;
487     int n1, n2;
488     const char *s1, *s2;
489     bool found;
490     int extra = 1;
491 
492     switch (type) {
493     case CMP_BOOLEAN:
494 	check_user_definable(idx, BOOLWRITE);
495 	b1 = e1->tterm.Booleans[idx];
496 	switch (compare) {
497 	case C_DIFFERENCE:
498 	    b2 = next_entry->Booleans[idx];
499 	    if (!(no_boolean(b1) && no_boolean(b2)) && (b1 != b2))
500 		(void) printf("\t%s: %s%s%s.\n",
501 			      name,
502 			      dump_boolean(b1),
503 			      bool_sep,
504 			      dump_boolean(b2));
505 	    break;
506 
507 	case C_COMMON:
508 	    if (b1 != ABSENT_BOOLEAN) {
509 		found = TRUE;
510 		for_each_entry() {
511 		    b2 = next_entry->Booleans[idx];
512 		    if (b1 != b2) {
513 			found = FALSE;
514 			break;
515 		    }
516 		}
517 		if (found) {
518 		    (void) printf("\t%s= %s.\n", name, dump_boolean(b1));
519 		}
520 	    }
521 	    break;
522 
523 	case C_NAND:
524 	    if (b1 == ABSENT_BOOLEAN) {
525 		found = TRUE;
526 		for_each_entry() {
527 		    b2 = next_entry->Booleans[idx];
528 		    if (b1 != b2) {
529 			found = FALSE;
530 			break;
531 		    }
532 		}
533 		if (found) {
534 		    (void) printf("\t!%s.\n", name);
535 		}
536 	    }
537 	    break;
538 	}
539 	break;
540 
541     case CMP_NUMBER:
542 	check_user_definable(idx, NUMWRITE);
543 	n1 = e1->tterm.Numbers[idx];
544 	switch (compare) {
545 	case C_DIFFERENCE:
546 	    n2 = next_entry->Numbers[idx];
547 	    if (!(no_numeric(n1) && no_numeric(n2)) && n1 != n2) {
548 		dump_numeric(n1, buf1);
549 		dump_numeric(n2, buf2);
550 		(void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
551 	    }
552 	    break;
553 
554 	case C_COMMON:
555 	    if (n1 != ABSENT_NUMERIC) {
556 		found = TRUE;
557 		for_each_entry() {
558 		    n2 = next_entry->Numbers[idx];
559 		    if (n1 != n2) {
560 			found = FALSE;
561 			break;
562 		    }
563 		}
564 		if (found) {
565 		    dump_numeric(n1, buf1);
566 		    (void) printf("\t%s= %s.\n", name, buf1);
567 		}
568 	    }
569 	    break;
570 
571 	case C_NAND:
572 	    if (n1 == ABSENT_NUMERIC) {
573 		found = TRUE;
574 		for_each_entry() {
575 		    n2 = next_entry->Numbers[idx];
576 		    if (n1 != n2) {
577 			found = FALSE;
578 			break;
579 		    }
580 		}
581 		if (found) {
582 		    (void) printf("\t!%s.\n", name);
583 		}
584 	    }
585 	    break;
586 	}
587 	break;
588 
589     case CMP_STRING:
590 	check_user_definable(idx, STRWRITE);
591 	s1 = e1->tterm.Strings[idx];
592 	switch (compare) {
593 	case C_DIFFERENCE:
594 	    s2 = next_entry->Strings[idx];
595 	    if (!(no_string(s1) && no_string(s2)) && capcmp(idx, s1, s2)) {
596 		dump_string(s1, buf1);
597 		dump_string(s2, buf2);
598 		if (strcmp(buf1, buf2))
599 		    (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
600 	    }
601 	    break;
602 
603 	case C_COMMON:
604 	    if (s1 != ABSENT_STRING) {
605 		found = TRUE;
606 		for_each_entry() {
607 		    s2 = next_entry->Strings[idx];
608 		    if (capcmp(idx, s1, s2) != 0) {
609 			found = FALSE;
610 			break;
611 		    }
612 		}
613 		if (found) {
614 		    (void) printf("\t%s= '%s'.\n", name, TIC_EXPAND(s1));
615 		}
616 	    }
617 	    break;
618 
619 	case C_NAND:
620 	    if (s1 == ABSENT_STRING) {
621 		found = TRUE;
622 		for_each_entry() {
623 		    s2 = next_entry->Strings[idx];
624 		    if (s2 != s1) {
625 			found = FALSE;
626 			break;
627 		    }
628 		}
629 		if (found) {
630 		    (void) printf("\t!%s.\n", name);
631 		}
632 	    }
633 	    break;
634 	}
635 	break;
636 
637     case CMP_USE:
638 	/* unlike the other modes, this compares *all* use entries */
639 	switch (compare) {
640 	case C_DIFFERENCE:
641 	    if (!useeq(e1, e2)) {
642 		(void) fputs("\tuse: ", stdout);
643 		print_uses(e1, stdout);
644 		fputs(", ", stdout);
645 		print_uses(e2, stdout);
646 		fputs(".\n", stdout);
647 	    }
648 	    break;
649 
650 	case C_COMMON:
651 	    if (e1->nuses) {
652 		found = TRUE;
653 		for_each_entry() {
654 		    e2 = &entries[extra++];
655 		    if (e2->nuses != e1->nuses || !useeq(e1, e2)) {
656 			found = FALSE;
657 			break;
658 		    }
659 		}
660 		if (found) {
661 		    (void) fputs("\tuse: ", stdout);
662 		    print_uses(e1, stdout);
663 		    fputs(".\n", stdout);
664 		}
665 	    }
666 	    break;
667 
668 	case C_NAND:
669 	    if (!e1->nuses) {
670 		found = TRUE;
671 		for_each_entry() {
672 		    e2 = &entries[extra++];
673 		    if (e2->nuses != e1->nuses) {
674 			found = FALSE;
675 			break;
676 		    }
677 		}
678 		if (found) {
679 		    (void) printf("\t!use.\n");
680 		}
681 	    }
682 	    break;
683 	}
684     }
685 }
686 
687 /***************************************************************************
688  *
689  * Init string analysis
690  *
691  ***************************************************************************/
692 
693 #define DATA(from, to) { { from }, { to } }
694 #define DATAX()        DATA("", "")
695 
696 typedef struct {
697     const char from[8];
698     const char to[12];
699 } assoc;
700 
701 static const assoc std_caps[] =
702 {
703     /* these are specified by X.364 and iBCS2 */
704     DATA("\033c", "RIS"),	/* full reset */
705     DATA("\0337", "SC"),	/* save cursor */
706     DATA("\0338", "RC"),	/* restore cursor */
707     DATA("\033[r", "RSR"),	/* not an X.364 mnemonic */
708     DATA("\033[m", "SGR0"),	/* not an X.364 mnemonic */
709     DATA("\033[2J", "ED2"),	/* clear page */
710 
711     /* this group is specified by ISO 2022 */
712     DATA("\033(0", "ISO DEC G0"),	/* enable DEC graphics for G0 */
713     DATA("\033(A", "ISO UK G0"),	/* enable UK chars for G0 */
714     DATA("\033(B", "ISO US G0"),	/* enable US chars for G0 */
715     DATA("\033)0", "ISO DEC G1"),	/* enable DEC graphics for G1 */
716     DATA("\033)A", "ISO UK G1"),	/* enable UK chars for G1 */
717     DATA("\033)B", "ISO US G1"),	/* enable US chars for G1 */
718 
719     /* these are DEC private controls widely supported by emulators */
720     DATA("\033=", "DECPAM"),	/* application keypad mode */
721     DATA("\033>", "DECPNM"),	/* normal keypad mode */
722     DATA("\033<", "DECANSI"),	/* enter ANSI mode */
723     DATA("\033[!p", "DECSTR"),	/* soft reset */
724     DATA("\033 F", "S7C1T"),	/* 7-bit controls */
725 
726     DATAX()
727 };
728 
729 static const assoc std_modes[] =
730 /* ECMA \E[ ... [hl] modes recognized by many emulators */
731 {
732     DATA("2", "AM"),		/* keyboard action mode */
733     DATA("4", "IRM"),		/* insert/replace mode */
734     DATA("12", "SRM"),		/* send/receive mode */
735     DATA("20", "LNM"),		/* linefeed mode */
736     DATAX()
737 };
738 
739 static const assoc private_modes[] =
740 /* DEC \E[ ... [hl] modes recognized by many emulators */
741 {
742     DATA("1", "CKM"),		/* application cursor keys */
743     DATA("2", "ANM"),		/* set VT52 mode */
744     DATA("3", "COLM"),		/* 132-column mode */
745     DATA("4", "SCLM"),		/* smooth scroll */
746     DATA("5", "SCNM"),		/* reverse video mode */
747     DATA("6", "OM"),		/* origin mode */
748     DATA("7", "AWM"),		/* wraparound mode */
749     DATA("8", "ARM"),		/* auto-repeat mode */
750     DATAX()
751 };
752 
753 static const assoc ecma_highlights[] =
754 /* recognize ECMA attribute sequences */
755 {
756     DATA("0", "NORMAL"),	/* normal */
757     DATA("1", "+BOLD"),		/* bold on */
758     DATA("2", "+DIM"),		/* dim on */
759     DATA("3", "+ITALIC"),	/* italic on */
760     DATA("4", "+UNDERLINE"),	/* underline on */
761     DATA("5", "+BLINK"),	/* blink on */
762     DATA("6", "+FASTBLINK"),	/* fastblink on */
763     DATA("7", "+REVERSE"),	/* reverse on */
764     DATA("8", "+INVISIBLE"),	/* invisible on */
765     DATA("9", "+DELETED"),	/* deleted on */
766     DATA("10", "MAIN-FONT"),	/* select primary font */
767     DATA("11", "ALT-FONT-1"),	/* select alternate font 1 */
768     DATA("12", "ALT-FONT-2"),	/* select alternate font 2 */
769     DATA("13", "ALT-FONT-3"),	/* select alternate font 3 */
770     DATA("14", "ALT-FONT-4"),	/* select alternate font 4 */
771     DATA("15", "ALT-FONT-5"),	/* select alternate font 5 */
772     DATA("16", "ALT-FONT-6"),	/* select alternate font 6 */
773     DATA("17", "ALT-FONT-7"),	/* select alternate font 7 */
774     DATA("18", "ALT-FONT-1"),	/* select alternate font 1 */
775     DATA("19", "ALT-FONT-1"),	/* select alternate font 1 */
776     DATA("20", "FRAKTUR"),	/* Fraktur font */
777     DATA("21", "DOUBLEUNDER"),	/* double underline */
778     DATA("22", "-DIM"),		/* dim off */
779     DATA("23", "-ITALIC"),	/* italic off */
780     DATA("24", "-UNDERLINE"),	/* underline off */
781     DATA("25", "-BLINK"),	/* blink off */
782     DATA("26", "-FASTBLINK"),	/* fastblink off */
783     DATA("27", "-REVERSE"),	/* reverse off */
784     DATA("28", "-INVISIBLE"),	/* invisible off */
785     DATA("29", "-DELETED"),	/* deleted off */
786     DATAX()
787 };
788 
789 #undef DATA
790 
791 static int
skip_csi(const char * cap)792 skip_csi(const char *cap)
793 {
794     int result = 0;
795     if (cap[0] == '\033' && cap[1] == '[')
796 	result = 2;
797     else if (UChar(cap[0]) == 0233)
798 	result = 1;
799     return result;
800 }
801 
802 static bool
same_param(const char * table,const char * param,size_t length)803 same_param(const char *table, const char *param, size_t length)
804 {
805     bool result = FALSE;
806     if (strncmp(table, param, length) == 0) {
807 	result = !isdigit(UChar(param[length]));
808     }
809     return result;
810 }
811 
812 static char *
lookup_params(const assoc * table,char * dst,char * src)813 lookup_params(const assoc * table, char *dst, char *src)
814 {
815     char *result = NULL;
816     const char *ep = strtok(src, ";");
817 
818     if (ep != NULL) {
819 	const assoc *ap;
820 
821 	do {
822 	    bool found = FALSE;
823 
824 	    for (ap = table; ap->from[0]; ap++) {
825 		size_t tlen = strlen(ap->from);
826 
827 		if (same_param(ap->from, ep, tlen)) {
828 		    _nc_STRCAT(dst, ap->to, MAX_TERMINFO_LENGTH);
829 		    found = TRUE;
830 		    break;
831 		}
832 	    }
833 
834 	    if (!found)
835 		_nc_STRCAT(dst, ep, MAX_TERMINFO_LENGTH);
836 	    _nc_STRCAT(dst, ";", MAX_TERMINFO_LENGTH);
837 	} while
838 	    ((ep = strtok((char *) 0, ";")) != NULL);
839 
840 	dst[strlen(dst) - 1] = '\0';
841 
842 	result = dst;
843     }
844     return result;
845 }
846 
847 static void
analyze_string(const char * name,const char * cap,TERMTYPE2 * tp)848 analyze_string(const char *name, const char *cap, TERMTYPE2 *tp)
849 {
850     char buf2[MAX_TERMINFO_LENGTH + 1];
851     const char *sp;
852     const assoc *ap;
853     int tp_lines = tp->Numbers[2];
854 
855     if (!VALID_STRING(cap))
856 	return;
857     (void) printf("%s: ", name);
858 
859     for (sp = cap; *sp; sp++) {
860 	int i;
861 	int csi;
862 	size_t len = 0;
863 	size_t next;
864 	const char *expansion = NULL;
865 	char buf3[MAX_TERMINFO_LENGTH];
866 
867 	/* first, check other capabilities in this entry */
868 	for (i = 0; i < STRCOUNT; i++) {
869 	    const char *cp = tp->Strings[i];
870 
871 	    /* don't use function-key capabilities */
872 	    if (strnames[i] == NULL)
873 		continue;
874 	    if (strnames[i][0] == 'k' && strnames[i][1] == 'f')
875 		continue;
876 
877 	    if (VALID_STRING(cp) &&
878 		cp[0] != '\0' &&
879 		cp != cap) {
880 		if ((len = strlen(cp)) > MAX_TERMINFO_LENGTH)
881 		    len = MAX_TERMINFO_LENGTH;
882 		_nc_STRNCPY(buf2, sp, len);
883 		buf2[len] = '\0';
884 
885 		if (_nc_capcmp(cp, buf2))
886 		    continue;
887 
888 #define ISRS(s)	(!strncmp((s), "is", (size_t) 2) || !strncmp((s), "rs", (size_t) 2))
889 		/*
890 		 * Theoretically we just passed the test for translation
891 		 * (equality once the padding is stripped).  However, there
892 		 * are a few more hoops that need to be jumped so that
893 		 * identical pairs of initialization and reset strings
894 		 * don't just refer to each other.
895 		 */
896 		if (ISRS(name) || ISRS(strnames[i]))
897 		    if (cap < cp)
898 			continue;
899 #undef ISRS
900 
901 		expansion = strnames[i];
902 		break;
903 	    }
904 	}
905 
906 	/* now check the standard capabilities */
907 	if (!expansion) {
908 	    csi = skip_csi(sp);
909 	    for (ap = std_caps; ap->from[0]; ap++) {
910 		size_t adj = (size_t) (csi ? 2 : 0);
911 
912 		len = strlen(ap->from);
913 		if (csi && skip_csi(ap->from) != csi)
914 		    continue;
915 		if (len > adj
916 		    && strncmp(ap->from + adj, sp + csi, len - adj) == 0) {
917 		    expansion = ap->to;
918 		    len -= adj;
919 		    len += (size_t) csi;
920 		    break;
921 		}
922 	    }
923 	}
924 
925 	/* now check for standard-mode sequences */
926 	if (!expansion
927 	    && (csi = skip_csi(sp)) != 0
928 	    && (len = (strspn) (sp + csi, "0123456789;"))
929 	    && (len < sizeof(buf3))
930 	    && (next = (size_t) csi + len)
931 	    && ((sp[next] == 'h') || (sp[next] == 'l'))) {
932 
933 	    _nc_STRCPY(buf2,
934 		       ((sp[next] == 'h')
935 			? "ECMA+"
936 			: "ECMA-"),
937 		       sizeof(buf2));
938 	    _nc_STRNCPY(buf3, sp + csi, len);
939 	    buf3[len] = '\0';
940 
941 	    expansion = lookup_params(std_modes, buf2, buf3);
942 	}
943 
944 	/* now check for private-mode sequences */
945 	if (!expansion
946 	    && (csi = skip_csi(sp)) != 0
947 	    && sp[csi] == '?'
948 	    && (len = (strspn) (sp + csi + 1, "0123456789;"))
949 	    && (len < sizeof(buf3))
950 	    && (next = (size_t) csi + 1 + len)
951 	    && ((sp[next] == 'h') || (sp[next] == 'l'))) {
952 
953 	    _nc_STRCPY(buf2,
954 		       ((sp[next] == 'h')
955 			? "DEC+"
956 			: "DEC-"),
957 		       sizeof(buf2));
958 	    _nc_STRNCPY(buf3, sp + csi + 1, len);
959 	    buf3[len] = '\0';
960 
961 	    expansion = lookup_params(private_modes, buf2, buf3);
962 	}
963 
964 	/* now check for ECMA highlight sequences */
965 	if (!expansion
966 	    && (csi = skip_csi(sp)) != 0
967 	    && (len = (strspn) (sp + csi, "0123456789;")) != 0
968 	    && (len < sizeof(buf3))
969 	    && (next = (size_t) csi + len)
970 	    && sp[next] == 'm') {
971 
972 	    _nc_STRCPY(buf2, "SGR:", sizeof(buf2));
973 	    _nc_STRNCPY(buf3, sp + csi, len);
974 	    buf3[len] = '\0';
975 	    len += (size_t) csi + 1;
976 
977 	    expansion = lookup_params(ecma_highlights, buf2, buf3);
978 	}
979 
980 	if (!expansion
981 	    && (csi = skip_csi(sp)) != 0
982 	    && sp[csi] == 'm') {
983 	    len = (size_t) csi + 1;
984 	    _nc_STRCPY(buf2, "SGR:", sizeof(buf2));
985 	    _nc_STRCAT(buf2, ecma_highlights[0].to, sizeof(buf2));
986 	    expansion = buf2;
987 	}
988 
989 	/* now check for scroll region reset */
990 	if (!expansion
991 	    && (csi = skip_csi(sp)) != 0) {
992 	    if (sp[csi] == 'r') {
993 		expansion = "RSR";
994 		len = 1;
995 	    } else {
996 		_nc_SPRINTF(buf2, _nc_SLIMIT(sizeof(buf2)) "1;%dr", tp_lines);
997 		len = strlen(buf2);
998 		if (strncmp(buf2, sp + csi, len) == 0)
999 		    expansion = "RSR";
1000 	    }
1001 	    len += (size_t) csi;
1002 	}
1003 
1004 	/* now check for home-down */
1005 	if (!expansion
1006 	    && (csi = skip_csi(sp)) != 0) {
1007 	    _nc_SPRINTF(buf2, _nc_SLIMIT(sizeof(buf2)) "%d;1H", tp_lines);
1008 	    len = strlen(buf2);
1009 	    if (strncmp(buf2, sp + csi, len) == 0) {
1010 		expansion = "LL";
1011 	    } else {
1012 		_nc_SPRINTF(buf2, _nc_SLIMIT(sizeof(buf2)) "%dH", tp_lines);
1013 		len = strlen(buf2);
1014 		if (strncmp(buf2, sp + csi, len) == 0) {
1015 		    expansion = "LL";
1016 		}
1017 	    }
1018 	    len += (size_t) csi;
1019 	}
1020 
1021 	/* now look at the expansion we got, if any */
1022 	if (expansion) {
1023 	    printf("{%s}", expansion);
1024 	    sp += len - 1;
1025 	} else {
1026 	    /* couldn't match anything */
1027 	    buf2[0] = *sp;
1028 	    buf2[1] = '\0';
1029 	    fputs(TIC_EXPAND(buf2), stdout);
1030 	}
1031     }
1032     putchar('\n');
1033 }
1034 
1035 /***************************************************************************
1036  *
1037  * File comparison
1038  *
1039  ***************************************************************************/
1040 
1041 static void
file_comparison(int argc,char * argv[])1042 file_comparison(int argc, char *argv[])
1043 {
1044 #define MAXCOMPARE	2
1045     /* someday we may allow comparisons on more files */
1046     int filecount = 0;
1047     ENTRY *heads[MAXCOMPARE];
1048     ENTRY *qp, *rp;
1049     int i, n;
1050 
1051     memset(heads, 0, sizeof(heads));
1052     dump_init((char *) 0, F_LITERAL, S_TERMINFO,
1053 	      FALSE, 0, 65535, itrace, FALSE, FALSE, FALSE);
1054 
1055     for (n = 0; n < argc && n < MAXCOMPARE; n++) {
1056 	if (freopen(argv[n], "r", stdin) == NULL)
1057 	    _nc_err_abort("Can't open %s: %s", argv[n], strerror(errno));
1058 
1059 #if NO_LEAKS
1060 	entered[n].head = _nc_head;
1061 	entered[n].tail = _nc_tail;
1062 #endif
1063 	_nc_head = _nc_tail = NULL;
1064 
1065 	/* parse entries out of the source file */
1066 	_nc_set_source(argv[n]);
1067 	_nc_read_entry_source(stdin, NULL, TRUE, literal, NULLHOOK);
1068 
1069 	if (itrace)
1070 	    (void) fprintf(stderr, "Resolving file %d...\n", n - 0);
1071 
1072 	/* maybe do use resolution */
1073 	if (!_nc_resolve_uses2(!limited, literal)) {
1074 	    (void) fprintf(stderr,
1075 			   "There are unresolved use entries in %s:\n",
1076 			   argv[n]);
1077 	    for_entry_list(qp) {
1078 		if (qp->nuses) {
1079 		    (void) fputs(qp->tterm.term_names, stderr);
1080 		    (void) fputc('\n', stderr);
1081 		}
1082 	    }
1083 	    ExitProgram(EXIT_FAILURE);
1084 	}
1085 
1086 	heads[filecount] = _nc_head;
1087 	filecount++;
1088     }
1089 
1090     /* OK, all entries are in core.  Ready to do the comparison */
1091     if (itrace)
1092 	(void) fprintf(stderr, "Entries are now in core...\n");
1093 
1094     /* The entry-matching loop. Sigh, this is intrinsically quadratic. */
1095     for (qp = heads[0]; qp; qp = qp->next) {
1096 	for (rp = heads[1]; rp; rp = rp->next)
1097 	    if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
1098 		if (qp->ncrosslinks < MAX_CROSSLINKS)
1099 		    qp->crosslinks[qp->ncrosslinks] = rp;
1100 		qp->ncrosslinks++;
1101 
1102 		if (rp->ncrosslinks < MAX_CROSSLINKS)
1103 		    rp->crosslinks[rp->ncrosslinks] = qp;
1104 		rp->ncrosslinks++;
1105 	    }
1106     }
1107 
1108     /* now we have two circular lists with crosslinks */
1109     if (itrace)
1110 	(void) fprintf(stderr, "Name matches are done...\n");
1111 
1112     for (qp = heads[0]; qp; qp = qp->next) {
1113 	if (qp->ncrosslinks > 1) {
1114 	    (void) fprintf(stderr,
1115 			   "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
1116 			   _nc_first_name(qp->tterm.term_names),
1117 			   argv[0],
1118 			   qp->ncrosslinks,
1119 			   argv[1]);
1120 	    for (i = 0; i < qp->ncrosslinks; i++)
1121 		(void) fprintf(stderr,
1122 			       "\t%s\n",
1123 			       _nc_first_name((qp->crosslinks[i])->tterm.term_names));
1124 	}
1125     }
1126 
1127     for (rp = heads[1]; rp; rp = rp->next) {
1128 	if (rp->ncrosslinks > 1) {
1129 	    (void) fprintf(stderr,
1130 			   "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
1131 			   _nc_first_name(rp->tterm.term_names),
1132 			   argv[1],
1133 			   rp->ncrosslinks,
1134 			   argv[0]);
1135 	    for (i = 0; i < rp->ncrosslinks; i++)
1136 		(void) fprintf(stderr,
1137 			       "\t%s\n",
1138 			       _nc_first_name((rp->crosslinks[i])->tterm.term_names));
1139 	}
1140     }
1141 
1142     (void) printf("In file 1 (%s) only:\n", argv[0]);
1143     for (qp = heads[0]; qp; qp = qp->next)
1144 	if (qp->ncrosslinks == 0)
1145 	    (void) printf("\t%s\n",
1146 			  _nc_first_name(qp->tterm.term_names));
1147 
1148     (void) printf("In file 2 (%s) only:\n", argv[1]);
1149     for (rp = heads[1]; rp; rp = rp->next)
1150 	if (rp->ncrosslinks == 0)
1151 	    (void) printf("\t%s\n",
1152 			  _nc_first_name(rp->tterm.term_names));
1153 
1154     (void) printf("The following entries are equivalent:\n");
1155     for (qp = heads[0]; qp; qp = qp->next) {
1156 	if (qp->ncrosslinks == 1) {
1157 	    rp = qp->crosslinks[0];
1158 
1159 	    repair_acsc(&qp->tterm);
1160 	    repair_acsc(&rp->tterm);
1161 #if NCURSES_XNAMES
1162 	    _nc_align_termtype(&qp->tterm, &rp->tterm);
1163 #endif
1164 	    if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) {
1165 		char name1[NAMESIZE], name2[NAMESIZE];
1166 
1167 		canonical_name(qp->tterm.term_names, name1);
1168 		canonical_name(rp->tterm.term_names, name2);
1169 
1170 		(void) printf("%s = %s\n", name1, name2);
1171 	    }
1172 	}
1173     }
1174 
1175     (void) printf("Differing entries:\n");
1176     termcount = 2;
1177     for (qp = heads[0]; qp; qp = qp->next) {
1178 
1179 	if (qp->ncrosslinks == 1) {
1180 	    rp = qp->crosslinks[0];
1181 #if NCURSES_XNAMES
1182 	    /* sorry - we have to do this on each pass */
1183 	    _nc_align_termtype(&qp->tterm, &rp->tterm);
1184 #endif
1185 	    if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) {
1186 		char name1[NAMESIZE], name2[NAMESIZE];
1187 		char *names[3];
1188 
1189 		names[0] = name1;
1190 		names[1] = name2;
1191 		names[2] = NULL;
1192 
1193 		entries[0] = *qp;
1194 		entries[1] = *rp;
1195 
1196 		canonical_name(qp->tterm.term_names, name1);
1197 		canonical_name(rp->tterm.term_names, name2);
1198 
1199 		switch (compare) {
1200 		case C_DIFFERENCE:
1201 		    show_comparing(names);
1202 		    compare_entry(compare_predicate, &entries->tterm, quiet);
1203 		    break;
1204 
1205 		case C_COMMON:
1206 		    show_comparing(names);
1207 		    compare_entry(compare_predicate, &entries->tterm, quiet);
1208 		    break;
1209 
1210 		case C_NAND:
1211 		    show_comparing(names);
1212 		    compare_entry(compare_predicate, &entries->tterm, quiet);
1213 		    break;
1214 
1215 		}
1216 	    }
1217 	}
1218     }
1219 }
1220 
1221 static void
usage(void)1222 usage(void)
1223 {
1224 #define DATA(s) s "\n"
1225     static const char head[] =
1226     {
1227 	DATA("")
1228 	DATA("Options:")
1229     };
1230 #undef DATA
1231     /* length is given here so the compiler can make everything readonly */
1232 #define DATA(s) s
1233     static const char options[][46] =
1234     {
1235 	"  -0    print single-row"
1236 	,"  -1    print single-column"
1237 	,"  -C    use termcap-names"
1238 	,"  -D    print database locations"
1239 	,"  -E    format output as C tables"
1240 	,"  -F    compare terminfo-files"
1241 	,"  -G    format %{number} to %'char'"
1242 	,"  -I    use terminfo-names"
1243 	,"  -K    use termcap-names and BSD syntax"
1244 	,"  -L    use long names"
1245 	,"  -R subset (see man page)"
1246 	,"  -T    eliminate size limits (test)"
1247 	,"  -U    do not post-process entries"
1248 	,"  -V    print version"
1249 	,"  -W    wrap long strings per -w[n]"
1250 #if NCURSES_XNAMES
1251 	,"  -a    with -F, list commented-out caps"
1252 #endif
1253 	,"  -c    list common capabilities"
1254 	,"  -d    list different capabilities"
1255 	,"  -e    format output for C initializer"
1256 	,"  -f    with -1, format complex strings"
1257 	,"  -g    format %'char' to %{number}"
1258 	,"  -i    analyze initialization/reset"
1259 	,"  -l    output terminfo names"
1260 	,"  -n    list capabilities in neither"
1261 	,"  -p    ignore padding specifiers"
1262 	,"  -Q number  dump compiled description"
1263 	,"  -q    brief listing, removes headers"
1264 	,"  -r    with -C, output in termcap form"
1265 	,"  -r    with -F, resolve use-references"
1266 	,"  -s [d|i|l|c] sort fields"
1267 #if NCURSES_XNAMES
1268 	,"  -t    suppress commented-out capabilities"
1269 #endif
1270 	,"  -u    produce source with 'use='"
1271 	,"  -v number  (verbose)"
1272 	,"  -w number  (width)"
1273 #if NCURSES_XNAMES
1274 	,"  -x    unknown capabilities are user-defined"
1275 #endif
1276     };
1277 #undef DATA
1278     const size_t last = SIZEOF(options);
1279     const size_t left = (last + 1) / 2;
1280     size_t n;
1281     FILE *fp = stderr;
1282 
1283     fprintf(fp, usage_string, _nc_progname);
1284     fputs(head, fp);
1285     for (n = 0; n < left; n++) {
1286 	size_t m = n + left;
1287 	if (m < last)
1288 	    fprintf(fp, "%-40.40s%s\n", options[n], options[m]);
1289 	else
1290 	    fprintf(fp, "%s\n", options[n]);
1291     }
1292     ExitProgram(EXIT_FAILURE);
1293 }
1294 
1295 #define isName(c) ((c) == '_' || isalnum(UChar(c)))
1296 
1297 static char *
safe_name(const char * format,const char * prefix,const char * name)1298 safe_name(const char *format, const char *prefix, const char *name)
1299 {
1300     static char *result;
1301     static size_t need;
1302     char *s;
1303 
1304     if (result == NULL) {
1305 	need = (strlen(prefix)
1306 		+ strlen(name)
1307 		+ strlen(format));
1308 	result = (char *) malloc(need + 1);
1309 	if (result == NULL)
1310 	    failed("safe_name");
1311     }
1312 
1313     _nc_STRCPY(result, "", need);
1314     if (isdigit(UChar(*prefix)))
1315 	_nc_STRCAT(result, "ti_", need);
1316     _nc_STRCAT(result, prefix, need);
1317     for (s = result; *s != 0 && *s != '|'; s++) {
1318 	if (!isName(*s))
1319 	    *s = '_';
1320     }
1321     *s = 0;
1322     if (isdigit(UChar(*name)) && !*prefix)
1323 	*s++ = '_';
1324     _nc_SPRINTF(s, _nc_SLIMIT(need) format, name);
1325     return result;
1326 }
1327 
1328 /*
1329  * escape contents of a double-quoted string.
1330  */
1331 static char *
safe_string(const char * source)1332 safe_string(const char *source)
1333 {
1334     static char *result;
1335     static size_t need;
1336     char *d;
1337     if (result == NULL) {
1338 	need = 2 * strlen(source) + 1;
1339 	result = (char *) malloc(need + 1);
1340     }
1341     for (d = result; *source != '\0'; ++source) {
1342 	char ch = *source;
1343 	if (ch == '"' || ch == '\\') {
1344 	    *d++ = '\\';
1345 	}
1346 	*d++ = ch;
1347     }
1348     *d = '\0';
1349     return result;
1350 }
1351 
1352 static char *
any_initializer(const char * fmt,const char * type)1353 any_initializer(const char *fmt, const char *type)
1354 {
1355     return safe_name(fmt, entries->tterm.term_names, type);
1356 }
1357 
1358 static char *
name_initializer(const char * type)1359 name_initializer(const char *type)
1360 {
1361     return any_initializer("_%s_data", type);
1362 }
1363 
1364 static char *
string_variable(const char * type)1365 string_variable(const char *type)
1366 {
1367     return any_initializer("_s_%s", type);
1368 }
1369 
1370 #if NCURSES_XNAMES
1371 static char *
name_of(const char * name)1372 name_of(const char *name)
1373 {
1374     return safe_name("name_of_%s", "", name);
1375 }
1376 
1377 static void
dump_extended_name(const char * name)1378 dump_extended_name(const char *name)
1379 {
1380     static char **known;
1381     static size_t dumped;
1382     static size_t length;
1383 
1384     if (name != NULL) {
1385 	bool found = FALSE;
1386 	if (length != 0) {
1387 	    size_t check;
1388 	    for (check = 0; check < dumped; ++check) {
1389 		if (!strcmp(name, known[check])) {
1390 		    found = TRUE;
1391 		    break;
1392 		}
1393 	    }
1394 	}
1395 	if (!found) {
1396 	    if (dumped + 2 > length) {
1397 		length += 100;
1398 		known = realloc(known, length * sizeof(*known));
1399 	    }
1400 	    printf("\n");
1401 	    printf("#ifndef %s\n", safe_name("extension_%s", "", name));
1402 	    printf("static char %s[] = \"%s\";\n", name_of(name), name);
1403 	    printf("#define %s 1\n", safe_name("extension_%s", "", name));
1404 	    printf("#endif\n");
1405 	    known[dumped] = strdup(name);
1406 	}
1407     } else {
1408 	while (dumped != 0) {
1409 	    free(known[--dumped]);
1410 	}
1411 	free(known);
1412 	length = 0;
1413     }
1414 }
1415 #endif
1416 
1417 /* dump C initializers for the terminal type */
1418 static void
dump_initializers(const TERMTYPE2 * term)1419 dump_initializers(const TERMTYPE2 *term)
1420 {
1421     unsigned n;
1422     const char *str = NULL;
1423 
1424     printf("\nstatic char %s[] = \"%s\";\n\n",
1425 	   name_initializer("alias"), safe_string(entries->tterm.term_names));
1426 
1427     for_each_string(n, term) {
1428 	if (VALID_STRING(term->Strings[n])) {
1429 	    char buf[MAX_STRING], *sp, *tp;
1430 
1431 	    tp = buf;
1432 #define TP_LIMIT	((MAX_STRING - 6) - (size_t)(tp - buf))
1433 	    *tp++ = '"';
1434 	    for (sp = term->Strings[n];
1435 		 *sp != 0 && TP_LIMIT > 5;
1436 		 sp++) {
1437 		if (isascii(UChar(*sp))
1438 		    && isprint(UChar(*sp))
1439 		    && *sp != '\\'
1440 		    && *sp != '"')
1441 		    *tp++ = *sp;
1442 		else {
1443 		    _nc_SPRINTF(tp, _nc_SLIMIT(TP_LIMIT) "\\%03o", UChar(*sp));
1444 		    tp += 4;
1445 		}
1446 	    }
1447 	    *tp++ = '"';
1448 	    *tp = '\0';
1449 	    (void) printf("static char %-20s[] = %s;\n",
1450 			  string_variable(ExtStrname(term, (int) n, strnames)),
1451 			  buf);
1452 	}
1453     }
1454     printf("\n");
1455 
1456     (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
1457 
1458     for_each_boolean(n, term) {
1459 	switch ((int) (term->Booleans[n])) {
1460 	case TRUE:
1461 	    str = "TRUE";
1462 	    break;
1463 
1464 	case FALSE:
1465 	    str = "FALSE";
1466 	    break;
1467 
1468 	case ABSENT_BOOLEAN:
1469 	    str = "ABSENT_BOOLEAN";
1470 	    break;
1471 
1472 	case CANCELLED_BOOLEAN:
1473 	    str = "CANCELLED_BOOLEAN";
1474 	    break;
1475 	}
1476 	(void) printf("\t/* %3u: %-8s */\t%s,\n",
1477 		      n, ExtBoolname(term, (int) n, boolnames), str);
1478     }
1479     (void) printf("%s;\n", R_CURL);
1480 
1481     (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
1482 
1483     for_each_number(n, term) {
1484 	char buf[BUFSIZ];
1485 	switch (term->Numbers[n]) {
1486 	case ABSENT_NUMERIC:
1487 	    str = "ABSENT_NUMERIC";
1488 	    break;
1489 	case CANCELLED_NUMERIC:
1490 	    str = "CANCELLED_NUMERIC";
1491 	    break;
1492 	default:
1493 	    _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) "%d", term->Numbers[n]);
1494 	    str = buf;
1495 	    break;
1496 	}
1497 	(void) printf("\t/* %3u: %-8s */\t%s,\n", n,
1498 		      ExtNumname(term, (int) n, numnames), str);
1499     }
1500     (void) printf("%s;\n", R_CURL);
1501 
1502     (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
1503 
1504     for_each_string(n, term) {
1505 
1506 	if (term->Strings[n] == ABSENT_STRING)
1507 	    str = "ABSENT_STRING";
1508 	else if (term->Strings[n] == CANCELLED_STRING)
1509 	    str = "CANCELLED_STRING";
1510 	else {
1511 	    str = string_variable(ExtStrname(term, (int) n, strnames));
1512 	}
1513 	(void) printf("\t/* %3u: %-8s */\t%s,\n", n,
1514 		      ExtStrname(term, (int) n, strnames), str);
1515     }
1516     (void) printf("%s;\n", R_CURL);
1517 
1518 #if NCURSES_XNAMES
1519     if ((NUM_BOOLEANS(term) != BOOLCOUNT)
1520 	|| (NUM_NUMBERS(term) != NUMCOUNT)
1521 	|| (NUM_STRINGS(term) != STRCOUNT)) {
1522 	for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
1523 	    dump_extended_name(ExtBoolname(term, (int) n, boolnames));
1524 	}
1525 	for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
1526 	    dump_extended_name(ExtNumname(term, (int) n, numnames));
1527 	}
1528 	for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
1529 	    dump_extended_name(ExtStrname(term, (int) n, strnames));
1530 	}
1531 	printf("\n");
1532 	(void) printf("static char * %s[] = %s\n",
1533 		      name_initializer("string_ext"), L_CURL);
1534 	for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
1535 	    (void) printf("\t/* %3u: bool */\t%s,\n",
1536 			  n, name_of(ExtBoolname(term, (int) n, boolnames)));
1537 	}
1538 	for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
1539 	    (void) printf("\t/* %3u: num */\t%s,\n",
1540 			  n, name_of(ExtNumname(term, (int) n, numnames)));
1541 	}
1542 	for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
1543 	    (void) printf("\t/* %3u: str */\t%s,\n",
1544 			  n, name_of(ExtStrname(term, (int) n, strnames)));
1545 	}
1546 	(void) printf("%s;\n", R_CURL);
1547     }
1548 #endif
1549 }
1550 
1551 /* dump C initializers for the terminal type */
1552 static void
dump_termtype(const TERMTYPE2 * term)1553 dump_termtype(const TERMTYPE2 *term)
1554 {
1555     (void) printf("\t%s\n\t\t%s,\n", L_CURL, name_initializer("alias"));
1556     (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
1557 
1558     (void) printf("\t\t%s,\n", name_initializer("bool"));
1559     (void) printf("\t\t%s,\n", name_initializer("number"));
1560 
1561     (void) printf("\t\t%s,\n", name_initializer("string"));
1562 
1563 #if NCURSES_XNAMES
1564     (void) printf("#if NCURSES_XNAMES\n");
1565     (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
1566     (void) printf("\t\t%s,\t/* ...corresponding names */\n",
1567 		  ((NUM_BOOLEANS(term) != BOOLCOUNT)
1568 		   || (NUM_NUMBERS(term) != NUMCOUNT)
1569 		   || (NUM_STRINGS(term) != STRCOUNT))
1570 		  ? name_initializer("string_ext")
1571 		  : "(char **)0");
1572 
1573     (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
1574     (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
1575     (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
1576 
1577     (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
1578 		  NUM_BOOLEANS(term) - BOOLCOUNT);
1579     (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
1580 		  NUM_NUMBERS(term) - NUMCOUNT);
1581     (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
1582 		  NUM_STRINGS(term) - STRCOUNT);
1583 
1584     (void) printf("#endif /* NCURSES_XNAMES */\n");
1585 #else
1586     (void) term;
1587 #endif /* NCURSES_XNAMES */
1588     (void) printf("\t%s\n", R_CURL);
1589 }
1590 
1591 static int
optarg_to_number(void)1592 optarg_to_number(void)
1593 {
1594     char *temp = NULL;
1595     long value = strtol(optarg, &temp, 0);
1596 
1597     if (temp == NULL || temp == optarg || *temp != 0) {
1598 	fprintf(stderr, "Expected a number, not \"%s\"\n", optarg);
1599 	ExitProgram(EXIT_FAILURE);
1600     }
1601     return (int) value;
1602 }
1603 
1604 static char *
terminal_env(void)1605 terminal_env(void)
1606 {
1607     char *terminal;
1608 
1609     if ((terminal = getenv("TERM")) == NULL) {
1610 	(void) fprintf(stderr,
1611 		       "%s: environment variable TERM not set\n",
1612 		       _nc_progname);
1613 	exit(EXIT_FAILURE);
1614     }
1615     return terminal;
1616 }
1617 
1618 /*
1619  * Show the databases that infocmp knows about.  The location to which it writes is
1620  */
1621 static void
show_databases(void)1622 show_databases(void)
1623 {
1624     DBDIRS state;
1625     int offset;
1626     const char *path2;
1627 
1628     _nc_first_db(&state, &offset);
1629     while ((path2 = _nc_next_db(&state, &offset)) != NULL) {
1630 	printf("%s\n", path2);
1631     }
1632     _nc_last_db();
1633 }
1634 
1635 /***************************************************************************
1636  *
1637  * Main sequence
1638  *
1639  ***************************************************************************/
1640 
1641 #if NO_LEAKS
1642 #define MAIN_LEAKS() \
1643     _nc_free_termtype2(&entries[0].tterm); \
1644     _nc_free_termtype2(&entries[1].tterm); \
1645     free(myargv); \
1646     free(tfile); \
1647     free(tname)
1648 #else
1649 #define MAIN_LEAKS()		/* nothing */
1650 #endif
1651 
1652 int
main(int argc,char * argv[])1653 main(int argc, char *argv[])
1654 {
1655     /* Avoid "local data >32k" error with mwcc */
1656     /* Also avoid overflowing smaller stacks on systems like AmigaOS */
1657     path *tfile = NULL;
1658     char **tname = NULL;
1659     size_t maxterms;
1660 
1661     char **myargv;
1662 
1663     char *firstdir, *restdir;
1664     int c;
1665     bool formatted = FALSE;
1666     bool filecompare = FALSE;
1667     int initdump = 0;
1668     bool init_analyze = FALSE;
1669     bool suppress_untranslatable = FALSE;
1670     int quickdump = 0;
1671     bool wrap_strings = FALSE;
1672 
1673     /* where is the terminfo database location going to default to? */
1674     restdir = firstdir = NULL;
1675 
1676 #if NCURSES_XNAMES
1677     use_extended_names(FALSE);
1678 #endif
1679     _nc_strict_bsd = 0;
1680 
1681     _nc_progname = _nc_rootname(argv[0]);
1682 
1683     /* make sure we have enough space to add two terminal entries */
1684     myargv = typeCalloc(char *, (size_t) (argc + 3));
1685     if (myargv == NULL)
1686 	failed("myargv");
1687 
1688     memcpy(myargv, argv, (sizeof(char *) * (size_t) argc));
1689     argv = myargv;
1690 
1691     while ((c = getopt(argc,
1692 		       argv,
1693 		       "01A:aB:CcDdEeFfGgIiKLlnpQ:qR:rs:TtUuVv:Ww:x")) != -1) {
1694 	switch (c) {
1695 	case '0':
1696 	    mwidth = 65535;
1697 	    mheight = 1;
1698 	    break;
1699 
1700 	case '1':
1701 	    mwidth = 0;
1702 	    break;
1703 
1704 	case 'A':
1705 	    firstdir = optarg;
1706 	    break;
1707 
1708 #if NCURSES_XNAMES
1709 	case 'a':
1710 	    _nc_disable_period = TRUE;
1711 	    use_extended_names(TRUE);
1712 	    break;
1713 #endif
1714 	case 'B':
1715 	    restdir = optarg;
1716 	    break;
1717 
1718 	case 'K':
1719 	    _nc_strict_bsd = 1;
1720 	    /* FALLTHRU */
1721 	case 'C':
1722 	    outform = F_TERMCAP;
1723 	    tversion = "BSD";
1724 	    if (sortmode == S_DEFAULT)
1725 		sortmode = S_TERMCAP;
1726 	    break;
1727 
1728 	case 'D':
1729 	    show_databases();
1730 	    ExitProgram(EXIT_SUCCESS);
1731 	    break;
1732 
1733 	case 'c':
1734 	    compare = C_COMMON;
1735 	    break;
1736 
1737 	case 'd':
1738 	    compare = C_DIFFERENCE;
1739 	    break;
1740 
1741 	case 'E':
1742 	    initdump |= 2;
1743 	    break;
1744 
1745 	case 'e':
1746 	    initdump |= 1;
1747 	    break;
1748 
1749 	case 'F':
1750 	    filecompare = TRUE;
1751 	    break;
1752 
1753 	case 'f':
1754 	    formatted = TRUE;
1755 	    break;
1756 
1757 	case 'G':
1758 	    numbers = 1;
1759 	    break;
1760 
1761 	case 'g':
1762 	    numbers = -1;
1763 	    break;
1764 
1765 	case 'I':
1766 	    outform = F_TERMINFO;
1767 	    if (sortmode == S_DEFAULT)
1768 		sortmode = S_VARIABLE;
1769 	    tversion = NULL;
1770 	    break;
1771 
1772 	case 'i':
1773 	    init_analyze = TRUE;
1774 	    break;
1775 
1776 	case 'L':
1777 	    outform = F_VARIABLE;
1778 	    if (sortmode == S_DEFAULT)
1779 		sortmode = S_VARIABLE;
1780 	    break;
1781 
1782 	case 'l':
1783 	    outform = F_TERMINFO;
1784 	    break;
1785 
1786 	case 'n':
1787 	    compare = C_NAND;
1788 	    break;
1789 
1790 	case 'p':
1791 	    ignorepads = TRUE;
1792 	    break;
1793 
1794 	case 'Q':
1795 	    quickdump = optarg_to_number();
1796 	    break;
1797 
1798 	case 'q':
1799 	    quiet = TRUE;
1800 	    s_absent = "-";
1801 	    s_cancel = "@";
1802 	    bool_sep = ", ";
1803 	    break;
1804 
1805 	case 'R':
1806 	    tversion = optarg;
1807 	    break;
1808 
1809 	case 'r':
1810 	    tversion = NULL;
1811 	    break;
1812 
1813 	case 's':
1814 	    if (*optarg == 'd')
1815 		sortmode = S_NOSORT;
1816 	    else if (*optarg == 'i')
1817 		sortmode = S_TERMINFO;
1818 	    else if (*optarg == 'l')
1819 		sortmode = S_VARIABLE;
1820 	    else if (*optarg == 'c')
1821 		sortmode = S_TERMCAP;
1822 	    else {
1823 		(void) fprintf(stderr,
1824 			       "%s: unknown sort mode\n",
1825 			       _nc_progname);
1826 		ExitProgram(EXIT_FAILURE);
1827 	    }
1828 	    break;
1829 
1830 	case 'T':
1831 	    limited = FALSE;
1832 	    break;
1833 
1834 #if NCURSES_XNAMES
1835 	case 't':
1836 	    _nc_disable_period = FALSE;
1837 	    suppress_untranslatable = TRUE;
1838 	    break;
1839 #endif
1840 
1841 	case 'U':
1842 	    literal = TRUE;
1843 	    break;
1844 
1845 	case 'u':
1846 	    compare = C_USEALL;
1847 	    break;
1848 
1849 	case 'V':
1850 	    puts(curses_version());
1851 	    ExitProgram(EXIT_SUCCESS);
1852 
1853 	case 'v':
1854 	    itrace = (unsigned) optarg_to_number();
1855 	    use_verbosity(itrace);
1856 	    break;
1857 
1858 	case 'W':
1859 	    wrap_strings = TRUE;
1860 	    break;
1861 
1862 	case 'w':
1863 	    mwidth = optarg_to_number();
1864 	    break;
1865 
1866 #if NCURSES_XNAMES
1867 	case 'x':
1868 	    use_extended_names(TRUE);
1869 	    break;
1870 #endif
1871 
1872 	default:
1873 	    usage();
1874 	}
1875     }
1876 
1877     maxterms = (size_t) (argc + 2 - optind);
1878     if ((tfile = typeMalloc(path, maxterms)) == NULL)
1879 	failed("tfile");
1880     if ((tname = typeCalloc(char *, maxterms)) == NULL)
1881 	  failed("tname");
1882     if ((entries = typeCalloc(ENTRY, maxterms)) == NULL)
1883 	failed("entries");
1884 #if NO_LEAKS
1885     if ((entered = typeCalloc(ENTERED, maxterms)) == NULL)
1886 	failed("entered");
1887 #endif
1888 
1889     if (tfile == NULL
1890 	|| tname == NULL
1891 	|| entries == NULL) {
1892 	fprintf(stderr, "%s: not enough memory\n", _nc_progname);
1893 	ExitProgram(EXIT_FAILURE);
1894     }
1895 
1896     /* by default, sort by terminfo name */
1897     if (sortmode == S_DEFAULT)
1898 	sortmode = S_TERMINFO;
1899 
1900     /* make sure we have at least one terminal name to work with */
1901     if (optind >= argc)
1902 	argv[argc++] = terminal_env();
1903 
1904     /* if user is after a comparison, make sure we have two entries */
1905     if (compare != C_DEFAULT && optind >= argc - 1)
1906 	argv[argc++] = terminal_env();
1907 
1908     /* exactly one terminal name with no options means display it */
1909     /* exactly two terminal names with no options means do -d */
1910     if (compare == C_DEFAULT) {
1911 	switch (argc - optind) {
1912 	default:
1913 	    fprintf(stderr, "%s: too many names to compare\n", _nc_progname);
1914 	    ExitProgram(EXIT_FAILURE);
1915 	case 1:
1916 	    break;
1917 	case 2:
1918 	    compare = C_DIFFERENCE;
1919 	    break;
1920 	}
1921     }
1922 
1923     /* set up for display */
1924     dump_init(tversion, outform, sortmode,
1925 	      wrap_strings, mwidth, mheight, itrace,
1926 	      formatted, FALSE, quickdump);
1927 
1928     if (!filecompare) {
1929 	/* grab the entries */
1930 	termcount = 0;
1931 	for (; optind < argc; optind++) {
1932 	    const char *directory = termcount ? restdir : firstdir;
1933 	    int status;
1934 
1935 	    tname[termcount] = argv[optind];
1936 
1937 	    if (directory) {
1938 #if NCURSES_USE_DATABASE
1939 		_nc_SPRINTF(tfile[termcount],
1940 			    _nc_SLIMIT(sizeof(path))
1941 			    "%s/" LEAF_FMT "/%s",
1942 			    directory,
1943 			    UChar(*argv[optind]), argv[optind]);
1944 		if (itrace)
1945 		    (void) fprintf(stderr,
1946 				   "%s: reading entry %s from file %s\n",
1947 				   _nc_progname,
1948 				   argv[optind], tfile[termcount]);
1949 
1950 		status = _nc_read_file_entry(tfile[termcount],
1951 					     &entries[termcount].tterm);
1952 #else
1953 		(void) fprintf(stderr, "%s: terminfo files not supported\n",
1954 			       _nc_progname);
1955 		MAIN_LEAKS();
1956 		ExitProgram(EXIT_FAILURE);
1957 #endif
1958 	    } else {
1959 		if (itrace)
1960 		    (void) fprintf(stderr,
1961 				   "%s: reading entry %s from database\n",
1962 				   _nc_progname,
1963 				   tname[termcount]);
1964 
1965 		status = _nc_read_entry2(tname[termcount],
1966 					 tfile[termcount],
1967 					 &entries[termcount].tterm);
1968 	    }
1969 
1970 	    if (status <= 0) {
1971 		switch (status) {
1972 		case TGETENT_NO:
1973 		    (void) fprintf(stderr,
1974 				   "%s: error: no match in terminfo"
1975 				   " database for terminal type"
1976 				   " \"%s\"\n",
1977 				   _nc_progname,
1978 				   tname[termcount]);
1979 		    break;
1980 		case TGETENT_ERR:
1981 		    /*
1982 		     * Several database files might be checked; their
1983 		     * file names are not exposed via this API.  The
1984 		     * best we can do is report how the final one
1985 		     * attempted failed.  Also, tfile[termcount] is
1986 		     * deeply misleading when a hashed database is used.
1987 		     */
1988 		    (void) fprintf(stderr,
1989 				   "%s: error: unable to open terminfo"
1990 				   " database: %s\n",
1991 				   _nc_progname,
1992 				   strerror(errno));
1993 		    break;
1994 		default:
1995 		    assert(0 == "unhandled _nc_read_entry2 return");
1996 		}
1997 		MAIN_LEAKS();
1998 		ExitProgram(EXIT_FAILURE);
1999 	    }
2000 	    repair_acsc(&entries[termcount].tterm);
2001 	    termcount++;
2002 	}
2003 
2004 #if NCURSES_XNAMES
2005 	if (termcount > 1) {
2006 	    /*
2007 	     * User-defined capabilities in different terminal descriptions
2008 	     * may have the same name/type but different indices.  Line up
2009 	     * the names to use comparable indices.  We may have more than two
2010 	     * entries to compare when processing the "-u" option.
2011 	     */
2012 	    for (c = 1; c < termcount; ++c)
2013 		_nc_align_termtype(&entries[c].tterm, &entries[0].tterm);
2014 	}
2015 #endif
2016 
2017 	/* dump as C initializer for the terminal type */
2018 	if (initdump) {
2019 	    if (initdump & 1)
2020 		dump_termtype(&entries[0].tterm);
2021 	    if (initdump & 2)
2022 		dump_initializers(&entries[0].tterm);
2023 	}
2024 
2025 	/* analyze the init strings */
2026 	else if (init_analyze) {
2027 #undef CUR
2028 #define CUR	entries[0].tterm.
2029 	    analyze_string("is1", init_1string, &entries[0].tterm);
2030 	    analyze_string("is2", init_2string, &entries[0].tterm);
2031 	    analyze_string("is3", init_3string, &entries[0].tterm);
2032 	    analyze_string("rs1", reset_1string, &entries[0].tterm);
2033 	    analyze_string("rs2", reset_2string, &entries[0].tterm);
2034 	    analyze_string("rs3", reset_3string, &entries[0].tterm);
2035 	    analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
2036 	    analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
2037 	    analyze_string("smkx", keypad_xmit, &entries[0].tterm);
2038 	    analyze_string("rmkx", keypad_local, &entries[0].tterm);
2039 #undef CUR
2040 	} else {
2041 	    int i;
2042 	    int len;
2043 
2044 	    /*
2045 	     * Here's where the real work gets done
2046 	     */
2047 	    switch (compare) {
2048 	    case C_DEFAULT:
2049 		if (itrace)
2050 		    (void) fprintf(stderr,
2051 				   "%s: about to dump %s\n",
2052 				   _nc_progname,
2053 				   tname[0]);
2054 		if (!quiet)
2055 		    (void)
2056 			printf("#\tReconstructed via %s from file: %s\n",
2057 			       _nc_progname, tfile[0]);
2058 		dump_entry(&entries[0].tterm,
2059 			   suppress_untranslatable,
2060 			   limited,
2061 			   numbers,
2062 			   NULL);
2063 		len = show_entry();
2064 		if (itrace)
2065 		    (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len);
2066 		break;
2067 
2068 	    case C_DIFFERENCE:
2069 		show_comparing(tname);
2070 		compare_entry(compare_predicate, &entries->tterm, quiet);
2071 		break;
2072 
2073 	    case C_COMMON:
2074 		show_comparing(tname);
2075 		compare_entry(compare_predicate, &entries->tterm, quiet);
2076 		break;
2077 
2078 	    case C_NAND:
2079 		show_comparing(tname);
2080 		compare_entry(compare_predicate, &entries->tterm, quiet);
2081 		break;
2082 
2083 	    case C_USEALL:
2084 		if (itrace)
2085 		    (void) fprintf(stderr, "%s: dumping use entry\n", _nc_progname);
2086 		dump_entry(&entries[0].tterm,
2087 			   suppress_untranslatable,
2088 			   limited,
2089 			   numbers,
2090 			   use_predicate);
2091 		for (i = 1; i < termcount; i++)
2092 		    dump_uses(tname[i], !(outform == F_TERMCAP
2093 					  || outform == F_TCONVERR));
2094 		len = show_entry();
2095 		if (itrace)
2096 		    (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len);
2097 		break;
2098 	    }
2099 	}
2100     } else if (compare == C_USEALL) {
2101 	(void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
2102     } else if (compare == C_DEFAULT) {
2103 	(void) fprintf(stderr,
2104 		       "Use `" ACTUAL_TIC " -[CI] <file>' for this.\n");
2105     } else if (argc - optind != 2) {
2106 	(void) fprintf(stderr,
2107 		       "File comparison needs exactly two file arguments.\n");
2108     } else {
2109 	file_comparison(argc - optind, argv + optind);
2110     }
2111 
2112     MAIN_LEAKS();
2113     ExitProgram(EXIT_SUCCESS);
2114 }
2115 
2116 /* infocmp.c ends here */
2117