infocmp.c revision 1.22
1/*	$OpenBSD: infocmp.c,v 1.22 2015/11/11 02:52:46 deraadt Exp $	*/
2
3/****************************************************************************
4 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
5 *                                                                          *
6 * Permission is hereby granted, free of charge, to any person obtaining a  *
7 * copy of this software and associated documentation files (the            *
8 * "Software"), to deal in the Software without restriction, including      *
9 * without limitation the rights to use, copy, modify, merge, publish,      *
10 * distribute, distribute with modifications, sublicense, and/or sell       *
11 * copies of the Software, and to permit persons to whom the Software is    *
12 * furnished to do so, subject to the following conditions:                 *
13 *                                                                          *
14 * The above copyright notice and this permission notice shall be included  *
15 * in all copies or substantial portions of the Software.                   *
16 *                                                                          *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24 *                                                                          *
25 * Except as contained in this notice, the name(s) of the above copyright   *
26 * holders shall not be used in advertising or otherwise to promote the     *
27 * sale, use or other dealings in this Software without prior written       *
28 * authorization.                                                           *
29 ****************************************************************************/
30
31/****************************************************************************
32 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
33 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
34 *     and: Thomas E. Dickey                        1996-on                 *
35 ****************************************************************************/
36
37/*
38 *	infocmp.c -- decompile an entry, or compare two entries
39 *		written by Eric S. Raymond
40 *		and Thomas E Dickey
41 */
42
43#include <progs.priv.h>
44
45#include <dump_entry.h>
46
47MODULE_ID("$Id: infocmp.c,v 1.22 2015/11/11 02:52:46 deraadt Exp $")
48
49#define L_CURL "{"
50#define R_CURL "}"
51
52#define MAX_STRING	1024	/* maximum formatted string */
53
54const char *_nc_progname = "infocmp";
55
56typedef 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
65static ENTRY *entries;		/* terminfo entries */
66static int termcount;		/* count of terminal entries */
67
68static bool limited = TRUE;	/* "-r" option is not set */
69static bool quiet = FALSE;
70static bool literal = FALSE;
71static const char *bool_sep = ":";
72static const char *s_absent = "NULL";
73static const char *s_cancel = "NULL";
74static const char *tversion;	/* terminfo version selected */
75static int itrace;		/* trace flag for debugging */
76static int mwidth = 60;
77static int numbers = 0;		/* format "%'char'" to/from "%{number}" */
78static int outform = F_TERMINFO;	/* output format */
79static int sortmode;		/* sort_mode */
80
81/* main comparison mode */
82static int compare;
83#define C_DEFAULT	0	/* don't force comparison mode */
84#define C_DIFFERENCE	1	/* list differences between two terminals */
85#define C_COMMON	2	/* list common capabilities */
86#define C_NAND		3	/* list capabilities in neither terminal */
87#define C_USEALL	4	/* generate relative use-form entry */
88static bool ignorepads;		/* ignore pad prefixes when diffing */
89
90#if NO_LEAKS
91#undef ExitProgram
92static void ExitProgram(int code) GCC_NORETURN;
93/* prototype is to get gcc to accept the noreturn attribute */
94static void
95ExitProgram(int code)
96{
97    while (termcount-- > 0)
98	_nc_free_termtype(&entries[termcount].tterm);
99    _nc_leaks_dump_entry();
100    free(entries);
101    _nc_free_tic(code);
102}
103#endif
104
105static char *
106canonical_name(char *ptr, char *buf, size_t bufl)
107/* extract the terminal type's primary name */
108{
109    char *bp;
110
111    (void) strlcpy(buf, ptr, bufl);
112    if ((bp = strchr(buf, '|')) != 0)
113	*bp = '\0';
114
115    return (buf);
116}
117
118/***************************************************************************
119 *
120 * Predicates for dump function
121 *
122 ***************************************************************************/
123
124static int
125capcmp(PredIdx idx, const char *s, const char *t)
126/* capability comparison function */
127{
128    if (!VALID_STRING(s) && !VALID_STRING(t))
129	return (s != t);
130    else if (!VALID_STRING(s) || !VALID_STRING(t))
131	return (1);
132
133    if ((idx == acs_chars_index) || !ignorepads)
134	return (strcmp(s, t));
135    else
136	return (_nc_capcmp(s, t));
137}
138
139static int
140use_predicate(unsigned type, PredIdx idx)
141/* predicate function to use for use decompilation */
142{
143    ENTRY *ep;
144
145    switch (type) {
146    case BOOLEAN:
147	{
148	    int is_set = FALSE;
149
150	    /*
151	     * This assumes that multiple use entries are supposed
152	     * to contribute the logical or of their boolean capabilities.
153	     * This is true if we take the semantics of multiple uses to
154	     * be 'each capability gets the first non-default value found
155	     * in the sequence of use entries'.
156	     *
157	     * Note that cancelled or absent booleans are stored as FALSE,
158	     * unlike numbers and strings, whose cancelled/absent state is
159	     * recorded in the terminfo database.
160	     */
161	    for (ep = &entries[1]; ep < entries + termcount; ep++)
162		if (ep->tterm.Booleans[idx] == TRUE) {
163		    is_set = entries[0].tterm.Booleans[idx];
164		    break;
165		}
166	    if (is_set != entries[0].tterm.Booleans[idx])
167		return (!is_set);
168	    else
169		return (FAIL);
170	}
171
172    case NUMBER:
173	{
174	    int value = ABSENT_NUMERIC;
175
176	    /*
177	     * We take the semantics of multiple uses to be 'each
178	     * capability gets the first non-default value found
179	     * in the sequence of use entries'.
180	     */
181	    for (ep = &entries[1]; ep < entries + termcount; ep++)
182		if (VALID_NUMERIC(ep->tterm.Numbers[idx])) {
183		    value = ep->tterm.Numbers[idx];
184		    break;
185		}
186
187	    if (value != entries[0].tterm.Numbers[idx])
188		return (value != ABSENT_NUMERIC);
189	    else
190		return (FAIL);
191	}
192
193    case STRING:
194	{
195	    char *termstr, *usestr = ABSENT_STRING;
196
197	    termstr = entries[0].tterm.Strings[idx];
198
199	    /*
200	     * We take the semantics of multiple uses to be 'each
201	     * capability gets the first non-default value found
202	     * in the sequence of use entries'.
203	     */
204	    for (ep = &entries[1]; ep < entries + termcount; ep++)
205		if (ep->tterm.Strings[idx]) {
206		    usestr = ep->tterm.Strings[idx];
207		    break;
208		}
209
210	    if (usestr == ABSENT_STRING && termstr == ABSENT_STRING)
211		return (FAIL);
212	    else if (!usestr || !termstr || capcmp(idx, usestr, termstr))
213		return (TRUE);
214	    else
215		return (FAIL);
216	}
217    }
218
219    return (FALSE);		/* pacify compiler */
220}
221
222static bool
223useeq(ENTRY * e1, ENTRY * e2)
224/* are the use references in two entries equivalent? */
225{
226    unsigned i, j;
227
228    if (e1->nuses != e2->nuses)
229	return (FALSE);
230
231    /* Ugh...this is quadratic again */
232    for (i = 0; i < e1->nuses; i++) {
233	bool foundmatch = FALSE;
234
235	/* search second entry for given use reference */
236	for (j = 0; j < e2->nuses; j++)
237	    if (!strcmp(e1->uses[i].name, e2->uses[j].name)) {
238		foundmatch = TRUE;
239		break;
240	    }
241
242	if (!foundmatch)
243	    return (FALSE);
244    }
245
246    return (TRUE);
247}
248
249static bool
250entryeq(TERMTYPE *t1, TERMTYPE *t2)
251/* are two entries equivalent? */
252{
253    unsigned i;
254
255    for (i = 0; i < NUM_BOOLEANS(t1); i++)
256	if (t1->Booleans[i] != t2->Booleans[i])
257	    return (FALSE);
258
259    for (i = 0; i < NUM_NUMBERS(t1); i++)
260	if (t1->Numbers[i] != t2->Numbers[i])
261	    return (FALSE);
262
263    for (i = 0; i < NUM_STRINGS(t1); i++)
264	if (capcmp((PredIdx) i, t1->Strings[i], t2->Strings[i]))
265	    return (FALSE);
266
267    return (TRUE);
268}
269
270#define TIC_EXPAND(result) _nc_tic_expand(result, outform==F_TERMINFO, numbers)
271
272static void
273print_uses(ENTRY * ep, FILE *fp)
274/* print an entry's use references */
275{
276    unsigned i;
277
278    if (!ep->nuses)
279	fputs("NULL", fp);
280    else
281	for (i = 0; i < ep->nuses; i++) {
282	    fputs(ep->uses[i].name, fp);
283	    if (i < ep->nuses - 1)
284		fputs(" ", fp);
285	}
286}
287
288static const char *
289dump_boolean(int val)
290/* display the value of a boolean capability */
291{
292    switch (val) {
293    case ABSENT_BOOLEAN:
294	return (s_absent);
295    case CANCELLED_BOOLEAN:
296	return (s_cancel);
297    case FALSE:
298	return ("F");
299    case TRUE:
300	return ("T");
301    default:
302	return ("?");
303    }
304}
305
306static void
307dump_numeric(int val, char *buf, size_t bufl)
308/* display the value of a boolean capability */
309{
310    switch (val) {
311    case ABSENT_NUMERIC:
312	strlcpy(buf, s_absent, bufl);
313	break;
314    case CANCELLED_NUMERIC:
315	strlcpy(buf, s_cancel, bufl);
316	break;
317    default:
318	snprintf(buf, bufl, "%d", val);
319	break;
320    }
321}
322
323static void
324dump_string(char *val, char *buf, size_t bufl)
325/* display the value of a string capability */
326{
327    if (val == ABSENT_STRING)
328	strlcpy(buf, s_absent, bufl);
329    else if (val == CANCELLED_STRING)
330	strlcpy(buf, s_cancel, bufl);
331    else {
332	snprintf(buf, bufl, "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val));
333    }
334}
335
336static void
337compare_predicate(PredType type, PredIdx idx, const char *name)
338/* predicate function to use for entry difference reports */
339{
340    ENTRY *e1 = &entries[0];
341    ENTRY *e2 = &entries[1];
342    char buf1[MAX_STRING], buf2[MAX_STRING];
343    int b1, b2;
344    int n1, n2;
345    char *s1, *s2;
346
347    switch (type) {
348    case CMP_BOOLEAN:
349	b1 = e1->tterm.Booleans[idx];
350	b2 = e2->tterm.Booleans[idx];
351	switch (compare) {
352	case C_DIFFERENCE:
353	    if (!(b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN) && b1 != b2)
354		(void) printf("\t%s: %s%s%s.\n",
355			      name,
356			      dump_boolean(b1),
357			      bool_sep,
358			      dump_boolean(b2));
359	    break;
360
361	case C_COMMON:
362	    if (b1 == b2 && b1 != ABSENT_BOOLEAN)
363		(void) printf("\t%s= %s.\n", name, dump_boolean(b1));
364	    break;
365
366	case C_NAND:
367	    if (b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN)
368		(void) printf("\t!%s.\n", name);
369	    break;
370	}
371	break;
372
373    case CMP_NUMBER:
374	n1 = e1->tterm.Numbers[idx];
375	n2 = e2->tterm.Numbers[idx];
376	dump_numeric(n1, buf1, sizeof buf1);
377	dump_numeric(n2, buf2, sizeof buf2);
378	switch (compare) {
379	case C_DIFFERENCE:
380	    if (!((n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)) && n1 != n2)
381		(void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
382	    break;
383
384	case C_COMMON:
385	    if (n1 != ABSENT_NUMERIC && n2 != ABSENT_NUMERIC && n1 == n2)
386		(void) printf("\t%s= %s.\n", name, buf1);
387	    break;
388
389	case C_NAND:
390	    if (n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)
391		(void) printf("\t!%s.\n", name);
392	    break;
393	}
394	break;
395
396    case CMP_STRING:
397	s1 = e1->tterm.Strings[idx];
398	s2 = e2->tterm.Strings[idx];
399	switch (compare) {
400	case C_DIFFERENCE:
401	    if (capcmp(idx, s1, s2)) {
402		dump_string(s1, buf1, sizeof buf1);
403		dump_string(s2, buf2, sizeof buf2);
404		if (strcmp(buf1, buf2))
405		    (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
406	    }
407	    break;
408
409	case C_COMMON:
410	    if (s1 && s2 && !capcmp(idx, s1, s2))
411		(void) printf("\t%s= '%s'.\n", name, TIC_EXPAND(s1));
412	    break;
413
414	case C_NAND:
415	    if (!s1 && !s2)
416		(void) printf("\t!%s.\n", name);
417	    break;
418	}
419	break;
420
421    case CMP_USE:
422	/* unlike the other modes, this compares *all* use entries */
423	switch (compare) {
424	case C_DIFFERENCE:
425	    if (!useeq(e1, e2)) {
426		(void) fputs("\tuse: ", stdout);
427		print_uses(e1, stdout);
428		fputs(", ", stdout);
429		print_uses(e2, stdout);
430		fputs(".\n", stdout);
431	    }
432	    break;
433
434	case C_COMMON:
435	    if (e1->nuses && e2->nuses && useeq(e1, e2)) {
436		(void) fputs("\tuse: ", stdout);
437		print_uses(e1, stdout);
438		fputs(".\n", stdout);
439	    }
440	    break;
441
442	case C_NAND:
443	    if (!e1->nuses && !e2->nuses)
444		(void) printf("\t!use.\n");
445	    break;
446	}
447    }
448}
449
450/***************************************************************************
451 *
452 * Init string analysis
453 *
454 ***************************************************************************/
455
456typedef struct {
457    const char *from;
458    const char *to;
459} assoc;
460
461static const assoc std_caps[] =
462{
463    /* these are specified by X.364 and iBCS2 */
464    {"\033c", "RIS"},		/* full reset */
465    {"\0337", "SC"},		/* save cursor */
466    {"\0338", "RC"},		/* restore cursor */
467    {"\033[r", "RSR"},		/* not an X.364 mnemonic */
468    {"\033[m", "SGR0"},		/* not an X.364 mnemonic */
469    {"\033[2J", "ED2"},		/* clear page */
470
471    /* this group is specified by ISO 2022 */
472    {"\033(0", "ISO DEC G0"},	/* enable DEC graphics for G0 */
473    {"\033(A", "ISO UK G0"},	/* enable UK chars for G0 */
474    {"\033(B", "ISO US G0"},	/* enable US chars for G0 */
475    {"\033)0", "ISO DEC G1"},	/* enable DEC graphics for G1 */
476    {"\033)A", "ISO UK G1"},	/* enable UK chars for G1 */
477    {"\033)B", "ISO US G1"},	/* enable US chars for G1 */
478
479    /* these are DEC private controls widely supported by emulators */
480    {"\033=", "DECPAM"},	/* application keypad mode */
481    {"\033>", "DECPNM"},	/* normal keypad mode */
482    {"\033<", "DECANSI"},	/* enter ANSI mode */
483    {"\033[!p", "DECSTR"},	/* soft reset */
484    {"\033 F", "S7C1T"},	/* 7-bit controls */
485
486    {(char *) 0, (char *) 0}
487};
488
489static const assoc std_modes[] =
490/* ECMA \E[ ... [hl] modes recognized by many emulators */
491{
492    {"2", "AM"},		/* keyboard action mode */
493    {"4", "IRM"},		/* insert/replace mode */
494    {"12", "SRM"},		/* send/receive mode */
495    {"20", "LNM"},		/* linefeed mode */
496    {(char *) 0, (char *) 0}
497};
498
499static const assoc private_modes[] =
500/* DEC \E[ ... [hl] modes recognized by many emulators */
501{
502    {"1", "CKM"},		/* application cursor keys */
503    {"2", "ANM"},		/* set VT52 mode */
504    {"3", "COLM"},		/* 132-column mode */
505    {"4", "SCLM"},		/* smooth scroll */
506    {"5", "SCNM"},		/* reverse video mode */
507    {"6", "OM"},		/* origin mode */
508    {"7", "AWM"},		/* wraparound mode */
509    {"8", "ARM"},		/* auto-repeat mode */
510    {(char *) 0, (char *) 0}
511};
512
513static const assoc ecma_highlights[] =
514/* recognize ECMA attribute sequences */
515{
516    {"0", "NORMAL"},		/* normal */
517    {"1", "+BOLD"},		/* bold on */
518    {"2", "+DIM"},		/* dim on */
519    {"3", "+ITALIC"},		/* italic on */
520    {"4", "+UNDERLINE"},	/* underline on */
521    {"5", "+BLINK"},		/* blink on */
522    {"6", "+FASTBLINK"},	/* fastblink on */
523    {"7", "+REVERSE"},		/* reverse on */
524    {"8", "+INVISIBLE"},	/* invisible on */
525    {"9", "+DELETED"},		/* deleted on */
526    {"10", "MAIN-FONT"},	/* select primary font */
527    {"11", "ALT-FONT-1"},	/* select alternate font 1 */
528    {"12", "ALT-FONT-2"},	/* select alternate font 2 */
529    {"13", "ALT-FONT-3"},	/* select alternate font 3 */
530    {"14", "ALT-FONT-4"},	/* select alternate font 4 */
531    {"15", "ALT-FONT-5"},	/* select alternate font 5 */
532    {"16", "ALT-FONT-6"},	/* select alternate font 6 */
533    {"17", "ALT-FONT-7"},	/* select alternate font 7 */
534    {"18", "ALT-FONT-1"},	/* select alternate font 1 */
535    {"19", "ALT-FONT-1"},	/* select alternate font 1 */
536    {"20", "FRAKTUR"},		/* Fraktur font */
537    {"21", "DOUBLEUNDER"},	/* double underline */
538    {"22", "-DIM"},		/* dim off */
539    {"23", "-ITALIC"},		/* italic off */
540    {"24", "-UNDERLINE"},	/* underline off */
541    {"25", "-BLINK"},		/* blink off */
542    {"26", "-FASTBLINK"},	/* fastblink off */
543    {"27", "-REVERSE"},		/* reverse off */
544    {"28", "-INVISIBLE"},	/* invisible off */
545    {"29", "-DELETED"},		/* deleted off */
546    {(char *) 0, (char *) 0}
547};
548
549static int
550skip_csi(const char *cap)
551{
552    int result = 0;
553    if (cap[0] == '\033' && cap[1] == '[')
554	result = 2;
555    else if (UChar(cap[0]) == 0233)
556	result = 1;
557    return result;
558}
559
560static bool
561same_param(const char *table, const char *param, unsigned length)
562{
563    bool result = FALSE;
564    if (strncmp(table, param, length) == 0) {
565	result = !isdigit(UChar(param[length]));
566    }
567    return result;
568}
569
570static char *
571lookup_params(const assoc * table, char *dst, char *src, size_t dstlen)
572{
573    char *result = 0;
574    const char *ep = strtok(src, ";");
575
576    if (ep != 0) {
577	const assoc *ap;
578
579	do {
580	    bool found = FALSE;
581
582	    for (ap = table; ap->from; ap++) {
583		size_t tlen = strlen(ap->from);
584
585		if (same_param(ap->from, ep, tlen)) {
586		    (void) strlcat(dst, ap->to, dstlen);
587		    found = TRUE;
588		    break;
589		}
590	    }
591
592	    if (!found)
593		(void) strlcat(dst, ep, dstlen);
594	    (void) strlcat(dst, ";", dstlen);
595	} while
596	    ((ep = strtok((char *) 0, ";")));
597
598	if (dst[0] != '\0' && dst[strlen(dst) - 1] == ';')
599	    dst[strlen(dst) - 1] = '\0';
600
601	result = dst;
602    }
603    return result;
604}
605
606static void
607analyze_string(const char *name, const char *cap, TERMTYPE *tp)
608{
609    char buf2[MAX_TERMINFO_LENGTH];
610    const char *sp;
611    const assoc *ap;
612    int tp_lines = tp->Numbers[2];
613
614    if (cap == ABSENT_STRING || cap == CANCELLED_STRING)
615	return;
616    (void) printf("%s: ", name);
617
618    for (sp = cap; *sp; sp++) {
619	int i;
620	int csi;
621	size_t len = 0;
622	size_t next;
623	const char *expansion = 0;
624	char buf3[MAX_TERMINFO_LENGTH];
625
626	/* first, check other capabilities in this entry */
627	for (i = 0; i < STRCOUNT; i++) {
628	    char *cp = tp->Strings[i];
629
630	    /* don't use soft-key capabilities */
631	    if (strnames[i][0] == 'k' && strnames[i][0] == 'f')
632		continue;
633
634	    if (cp != ABSENT_STRING && cp != CANCELLED_STRING && cp[0] && cp
635		!= cap) {
636		len = strlen(cp);
637		(void) strncpy(buf2, sp, len);
638		buf2[len] = '\0';
639
640		if (_nc_capcmp(cp, buf2))
641		    continue;
642
643#define ISRS(s)	(!strncmp((s), "is", 2) || !strncmp((s), "rs", 2))
644		/*
645		 * Theoretically we just passed the test for translation
646		 * (equality once the padding is stripped).  However, there
647		 * are a few more hoops that need to be jumped so that
648		 * identical pairs of initialization and reset strings
649		 * don't just refer to each other.
650		 */
651		if (ISRS(name) || ISRS(strnames[i]))
652		    if (cap < cp)
653			continue;
654#undef ISRS
655
656		expansion = strnames[i];
657		break;
658	    }
659	}
660
661	/* now check the standard capabilities */
662	if (!expansion) {
663	    csi = skip_csi(sp);
664	    for (ap = std_caps; ap->from; ap++) {
665		size_t adj = (size_t) (csi ? 2 : 0);
666
667		len = strlen(ap->from);
668		if (csi && skip_csi(ap->from) != csi)
669		    continue;
670		if (len > adj
671		    && strncmp(ap->from + adj, sp + csi, len - adj) == 0) {
672		    expansion = ap->to;
673		    len -= adj;
674		    len += (size_t) csi;
675		    break;
676		}
677	    }
678	}
679
680	/* now check for standard-mode sequences */
681	if (!expansion
682	    && (csi = skip_csi(sp)) != 0
683	    && (len = strspn(sp + csi, "0123456789;"))
684	    && (len < sizeof(buf3))
685	    && (next = (size_t) csi + len)
686	    && ((sp[next] == 'h') || (sp[next] == 'l'))) {
687
688	    (void) strlcpy(buf2, (sp[next] == 'h') ? "ECMA+" : "ECMA-",
689		sizeof buf2);
690	    (void) strncpy(buf3, sp + csi, len);
691	    buf3[len] = '\0';
692	    len += (size_t) csi + 1;
693
694	    expansion = lookup_params(std_modes, buf2, buf3, sizeof buf2);
695	}
696
697	/* now check for private-mode sequences */
698	if (!expansion
699	    && (csi = skip_csi(sp)) != 0
700	    && sp[csi] == '?'
701	    && (len = strspn(sp + csi + 1, "0123456789;"))
702	    && (len < sizeof(buf3))
703	    && (next = (size_t) csi + 1 + len)
704	    && ((sp[next] == 'h') || (sp[next] == 'l'))) {
705
706	    (void) strlcpy(buf2, (sp[next] == 'h') ? "DEC+" : "DEC-",
707		sizeof buf2);
708	    (void) strncpy(buf3, sp + csi + 1, len);
709	    buf3[len] = '\0';
710	    len += (size_t) csi + 2;
711
712	    expansion = lookup_params(private_modes, buf2, buf3, sizeof buf2);
713	}
714
715	/* now check for ECMA highlight sequences */
716	if (!expansion
717	    && (csi = skip_csi(sp)) != 0
718	    && (len = strspn(sp + csi, "0123456789;")) != 0
719	    && (len < sizeof(buf3))
720	    && (next = (size_t) csi + len)
721	    && sp[next] == 'm') {
722
723	    (void) strlcpy(buf2, "SGR:", sizeof buf2);
724	    (void) strncpy(buf3, sp + csi, len);
725	    buf3[len] = '\0';
726	    len += (size_t) csi + 1;
727
728	    expansion = lookup_params(ecma_highlights, buf2, buf3, sizeof buf2);
729	}
730
731	if (!expansion
732	    && (csi = skip_csi(sp)) != 0
733	    && sp[csi] == 'm') {
734	    len = (size_t) csi + 1;
735	    (void) strlcpy(buf2, "SGR:", sizeof buf2);
736	    strlcat(buf2, ecma_highlights[0].to, sizeof buf2);
737	    expansion = buf2;
738	}
739
740	/* now check for scroll region reset */
741	if (!expansion
742	    && (csi = skip_csi(sp)) != 0) {
743	    if (sp[csi] == 'r') {
744		expansion = "RSR";
745		len = 1;
746	    } else {
747		(void) snprintf(buf2, sizeof buf2, "1;%dr", tp_lines);
748		len = strlen(buf2);
749		if (strncmp(buf2, sp + csi, len) == 0)
750		    expansion = "RSR";
751	    }
752	    len += (size_t) csi;
753	}
754
755	/* now check for home-down */
756	if (!expansion
757	    && (csi = skip_csi(sp)) != 0) {
758	    (void) snprintf(buf2, sizeof buf2, "%d;1H", tp_lines);
759	    len = strlen(buf2);
760	    if (strncmp(buf2, sp + csi, len) == 0) {
761		expansion = "LL";
762	    } else {
763		(void) snprintf(buf2, sizeof buf2, "%dH", tp_lines);
764		len = strlen(buf2);
765		if (strncmp(buf2, sp + csi, len) == 0) {
766		    expansion = "LL";
767		}
768	    }
769	    len += (size_t) csi;
770	}
771
772	/* now look at the expansion we got, if any */
773	if (expansion) {
774	    printf("{%s}", expansion);
775	    sp += len - 1;
776	} else {
777	    /* couldn't match anything */
778	    buf2[0] = *sp;
779	    buf2[1] = '\0';
780	    fputs(TIC_EXPAND(buf2), stdout);
781	}
782    }
783    putchar('\n');
784}
785
786/***************************************************************************
787 *
788 * File comparison
789 *
790 ***************************************************************************/
791
792static void
793file_comparison(int argc, char *argv[])
794{
795#define MAXCOMPARE	2
796    /* someday we may allow comparisons on more files */
797    int filecount = 0;
798    ENTRY *heads[MAXCOMPARE];
799    ENTRY *qp, *rp;
800    int i, n;
801
802    memset(heads, 0, sizeof(heads));
803    dump_init((char *) 0, F_LITERAL, S_TERMINFO, 0, itrace, FALSE);
804
805    for (n = 0; n < argc && n < MAXCOMPARE; n++) {
806	if (freopen(argv[n], "r", stdin) == 0)
807	    _nc_err_abort("Can't open %s", argv[n]);
808
809	_nc_head = _nc_tail = 0;
810
811	/* parse entries out of the source file */
812	_nc_set_source(argv[n]);
813	_nc_read_entry_source(stdin, NULL, TRUE, literal, NULLHOOK);
814
815	if (itrace)
816	    (void) fprintf(stderr, "Resolving file %d...\n", n - 0);
817
818	/* maybe do use resolution */
819	if (!_nc_resolve_uses2(!limited, literal)) {
820	    (void) fprintf(stderr,
821			   "There are unresolved use entries in %s:\n",
822			   argv[n]);
823	    for_entry_list(qp) {
824		if (qp->nuses) {
825		    (void) fputs(qp->tterm.term_names, stderr);
826		    (void) fputc('\n', stderr);
827		}
828	    }
829	    ExitProgram(EXIT_FAILURE);
830	}
831
832	heads[filecount] = _nc_head;
833	filecount++;
834    }
835
836    /* OK, all entries are in core.  Ready to do the comparison */
837    if (itrace)
838	(void) fprintf(stderr, "Entries are now in core...\n");
839
840    /* The entry-matching loop. Sigh, this is intrinsically quadratic. */
841    for (qp = heads[0]; qp; qp = qp->next) {
842	for (rp = heads[1]; rp; rp = rp->next)
843	    if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
844		if (qp->ncrosslinks < MAX_CROSSLINKS)
845		    qp->crosslinks[qp->ncrosslinks] = rp;
846		qp->ncrosslinks++;
847
848		if (rp->ncrosslinks < MAX_CROSSLINKS)
849		    rp->crosslinks[rp->ncrosslinks] = qp;
850		rp->ncrosslinks++;
851	    }
852    }
853
854    /* now we have two circular lists with crosslinks */
855    if (itrace)
856	(void) fprintf(stderr, "Name matches are done...\n");
857
858    for (qp = heads[0]; qp; qp = qp->next) {
859	if (qp->ncrosslinks > 1) {
860	    (void) fprintf(stderr,
861			   "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
862			   _nc_first_name(qp->tterm.term_names),
863			   argv[0],
864			   qp->ncrosslinks,
865			   argv[1]);
866	    for (i = 0; i < qp->ncrosslinks; i++)
867		(void) fprintf(stderr,
868			       "\t%s\n",
869			       _nc_first_name((qp->crosslinks[i])->tterm.term_names));
870	}
871    }
872
873    for (rp = heads[1]; rp; rp = rp->next) {
874	if (rp->ncrosslinks > 1) {
875	    (void) fprintf(stderr,
876			   "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
877			   _nc_first_name(rp->tterm.term_names),
878			   argv[1],
879			   rp->ncrosslinks,
880			   argv[0]);
881	    for (i = 0; i < rp->ncrosslinks; i++)
882		(void) fprintf(stderr,
883			       "\t%s\n",
884			       _nc_first_name((rp->crosslinks[i])->tterm.term_names));
885	}
886    }
887
888    (void) printf("In file 1 (%s) only:\n", argv[0]);
889    for (qp = heads[0]; qp; qp = qp->next)
890	if (qp->ncrosslinks == 0)
891	    (void) printf("\t%s\n",
892			  _nc_first_name(qp->tterm.term_names));
893
894    (void) printf("In file 2 (%s) only:\n", argv[1]);
895    for (rp = heads[1]; rp; rp = rp->next)
896	if (rp->ncrosslinks == 0)
897	    (void) printf("\t%s\n",
898			  _nc_first_name(rp->tterm.term_names));
899
900    (void) printf("The following entries are equivalent:\n");
901    for (qp = heads[0]; qp; qp = qp->next) {
902	rp = qp->crosslinks[0];
903
904	if (qp->ncrosslinks == 1) {
905	    rp = qp->crosslinks[0];
906
907	    repair_acsc(&qp->tterm);
908	    repair_acsc(&rp->tterm);
909#if NCURSES_XNAMES
910	    _nc_align_termtype(&qp->tterm, &rp->tterm);
911#endif
912	    if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) {
913		char name1[NAMESIZE], name2[NAMESIZE];
914
915		(void) canonical_name(qp->tterm.term_names, name1, sizeof name1);
916		(void) canonical_name(rp->tterm.term_names, name2, sizeof name2);
917
918		(void) printf("%s = %s\n", name1, name2);
919	    }
920	}
921    }
922
923    (void) printf("Differing entries:\n");
924    termcount = 2;
925    for (qp = heads[0]; qp; qp = qp->next) {
926
927	if (qp->ncrosslinks == 1) {
928	    rp = qp->crosslinks[0];
929#if NCURSES_XNAMES
930	    /* sorry - we have to do this on each pass */
931	    _nc_align_termtype(&qp->tterm, &rp->tterm);
932#endif
933	    if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) {
934		char name1[NAMESIZE], name2[NAMESIZE];
935
936		entries[0] = *qp;
937		entries[1] = *rp;
938
939		(void) canonical_name(qp->tterm.term_names, name1, sizeof name1);
940		(void) canonical_name(rp->tterm.term_names, name2, sizeof name2);
941
942		switch (compare) {
943		case C_DIFFERENCE:
944		    if (itrace)
945			(void) fprintf(stderr,
946				       "%s: dumping differences\n",
947				       _nc_progname);
948		    (void) printf("comparing %s to %s.\n", name1, name2);
949		    compare_entry(compare_predicate, &entries->tterm, quiet);
950		    break;
951
952		case C_COMMON:
953		    if (itrace)
954			(void) fprintf(stderr,
955				       "%s: dumping common capabilities\n",
956				       _nc_progname);
957		    (void) printf("comparing %s to %s.\n", name1, name2);
958		    compare_entry(compare_predicate, &entries->tterm, quiet);
959		    break;
960
961		case C_NAND:
962		    if (itrace)
963			(void) fprintf(stderr,
964				       "%s: dumping differences\n",
965				       _nc_progname);
966		    (void) printf("comparing %s to %s.\n", name1, name2);
967		    compare_entry(compare_predicate, &entries->tterm, quiet);
968		    break;
969
970		}
971	    }
972	}
973    }
974}
975
976static void
977usage(void)
978{
979    static const char *tbl[] =
980    {
981	"Usage: infocmp [options] [-A directory] [-B directory] [termname...]"
982	,""
983	,"Options:"
984	,"  -1    print single-column"
985	,"  -C    use termcap-names"
986	,"  -F    compare terminfo-files"
987	,"  -I    use terminfo-names"
988	,"  -L    use long names"
989	,"  -R subset (see manpage)"
990	,"  -T    eliminate size limits (test)"
991	,"  -U    eliminate post-processing of entries"
992	,"  -V    print version"
993#if NCURSES_XNAMES
994	,"  -a    with -F, list commented-out caps"
995#endif
996	,"  -c    list common capabilities"
997	,"  -d    list different capabilities"
998	,"  -e    format output for C initializer"
999	,"  -E    format output as C tables"
1000	,"  -f    with -1, format complex strings"
1001	,"  -G    format %{number} to %'char'"
1002	,"  -g    format %'char' to %{number}"
1003	,"  -i    analyze initialization/reset"
1004	,"  -l    output terminfo names"
1005	,"  -n    list capabilities in neither"
1006	,"  -p    ignore padding specifiers"
1007	,"  -q    brief listing, removes headers"
1008	,"  -r    with -C, output in termcap form"
1009	,"  -r    with -F, resolve use-references"
1010	,"  -s [d|i|l|c] sort fields"
1011#if NCURSES_XNAMES
1012	,"  -t    suppress commented-out capabilities"
1013#endif
1014	,"  -u    produce source with 'use='"
1015	,"  -v number  (verbose)"
1016	,"  -w number  (width)"
1017#if NCURSES_XNAMES
1018	,"  -x    treat unknown capabilities as user-defined"
1019#endif
1020    };
1021    const size_t first = 3;
1022    const size_t last = SIZEOF(tbl);
1023    const size_t left = (last - first + 1) / 2 + first;
1024    size_t n;
1025
1026    for (n = 0; n < left; n++) {
1027	size_t m = (n < first) ? last : n + left - first;
1028	if (m < last)
1029	    fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]);
1030	else
1031	    fprintf(stderr, "%s\n", tbl[n]);
1032    }
1033    ExitProgram(EXIT_FAILURE);
1034}
1035
1036static char *
1037any_initializer(const char *fmt, const char *type)
1038{
1039    static char *initializer;
1040    static size_t len;
1041    char *s;
1042
1043    if (initializer == 0) {
1044	len = strlen(entries->tterm.term_names) + strlen(type) + strlen(fmt);
1045	initializer = (char *) malloc(len);
1046    }
1047
1048    (void) strlcpy(initializer, entries->tterm.term_names, len);
1049    for (s = initializer; *s != 0 && *s != '|'; s++) {
1050	if (!isalnum(UChar(*s)))
1051	    *s = '_';
1052    }
1053    *s = 0;
1054    (void) snprintf(s, len - (s - initializer), fmt, type);
1055    return initializer;
1056}
1057
1058static char *
1059name_initializer(const char *type)
1060{
1061    return any_initializer("_%s_data", type);
1062}
1063
1064static char *
1065string_variable(const char *type)
1066{
1067    return any_initializer("_s_%s", type);
1068}
1069
1070/* dump C initializers for the terminal type */
1071static void
1072dump_initializers(TERMTYPE *term)
1073{
1074    unsigned n;
1075    const char *str = 0;
1076
1077    printf("\nstatic char %s[] = \"%s\";\n\n",
1078	   name_initializer("alias"), entries->tterm.term_names);
1079
1080    for_each_string(n, term) {
1081	char buf[MAX_STRING], *sp, *tp;
1082
1083	if (VALID_STRING(term->Strings[n])) {
1084	    tp = buf;
1085	    *tp++ = '"';
1086	    for (sp = term->Strings[n];
1087		 *sp != 0 && (tp - buf) < MAX_STRING - 6;
1088		 sp++) {
1089		if (isascii(UChar(*sp))
1090		    && isprint(UChar(*sp))
1091		    && *sp != '\\'
1092		    && *sp != '"')
1093		    *tp++ = *sp;
1094		else {
1095		    (void) snprintf(tp, buf + sizeof buf - tp, "\\%03o",
1096			UChar(*sp));
1097		    tp += strlen(tp);
1098		}
1099	    }
1100	    *tp++ = '"';
1101	    *tp = '\0';
1102	    (void) printf("static char %-20s[] = %s;\n",
1103			  string_variable(ExtStrname(term, n, strnames)), buf);
1104	}
1105    }
1106    printf("\n");
1107
1108    (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
1109
1110    for_each_boolean(n, term) {
1111	switch ((int) (term->Booleans[n])) {
1112	case TRUE:
1113	    str = "TRUE";
1114	    break;
1115
1116	case FALSE:
1117	    str = "FALSE";
1118	    break;
1119
1120	case ABSENT_BOOLEAN:
1121	    str = "ABSENT_BOOLEAN";
1122	    break;
1123
1124	case CANCELLED_BOOLEAN:
1125	    str = "CANCELLED_BOOLEAN";
1126	    break;
1127	}
1128	(void) printf("\t/* %3u: %-8s */\t%s,\n",
1129		      n, ExtBoolname(term, n, boolnames), str);
1130    }
1131    (void) printf("%s;\n", R_CURL);
1132
1133    (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
1134
1135    for_each_number(n, term) {
1136	char buf[BUFSIZ];
1137	switch (term->Numbers[n]) {
1138	case ABSENT_NUMERIC:
1139	    str = "ABSENT_NUMERIC";
1140	    break;
1141	case CANCELLED_NUMERIC:
1142	    str = "CANCELLED_NUMERIC";
1143	    break;
1144	default:
1145	    snprintf(buf, sizeof buf, "%d", term->Numbers[n]);
1146	    str = buf;
1147	    break;
1148	}
1149	(void) printf("\t/* %3u: %-8s */\t%s,\n", n,
1150		      ExtNumname(term, n, numnames), str);
1151    }
1152    (void) printf("%s;\n", R_CURL);
1153
1154    (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
1155
1156    for_each_string(n, term) {
1157
1158	if (term->Strings[n] == ABSENT_STRING)
1159	    str = "ABSENT_STRING";
1160	else if (term->Strings[n] == CANCELLED_STRING)
1161	    str = "CANCELLED_STRING";
1162	else {
1163	    str = string_variable(ExtStrname(term, n, strnames));
1164	}
1165	(void) printf("\t/* %3u: %-8s */\t%s,\n", n,
1166		      ExtStrname(term, n, strnames), str);
1167    }
1168    (void) printf("%s;\n", R_CURL);
1169
1170#if NCURSES_XNAMES
1171    if ((NUM_BOOLEANS(term) != BOOLCOUNT)
1172	|| (NUM_NUMBERS(term) != NUMCOUNT)
1173	|| (NUM_STRINGS(term) != STRCOUNT)) {
1174	(void) printf("static char * %s[] = %s\n",
1175		      name_initializer("string_ext"), L_CURL);
1176	for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
1177	    (void) printf("\t/* %3u: bool */\t\"%s\",\n",
1178			  n, ExtBoolname(term, n, boolnames));
1179	}
1180	for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
1181	    (void) printf("\t/* %3u: num */\t\"%s\",\n",
1182			  n, ExtNumname(term, n, numnames));
1183	}
1184	for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
1185	    (void) printf("\t/* %3u: str */\t\"%s\",\n",
1186			  n, ExtStrname(term, n, strnames));
1187	}
1188	(void) printf("%s;\n", R_CURL);
1189    }
1190#endif
1191}
1192
1193/* dump C initializers for the terminal type */
1194static void
1195dump_termtype(TERMTYPE *term)
1196{
1197    (void) printf("\t%s\n\t\t%s,\n", L_CURL, name_initializer("alias"));
1198    (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
1199
1200    (void) printf("\t\t%s,\n", name_initializer("bool"));
1201    (void) printf("\t\t%s,\n", name_initializer("number"));
1202
1203    (void) printf("\t\t%s,\n", name_initializer("string"));
1204
1205#if NCURSES_XNAMES
1206    (void) printf("#if NCURSES_XNAMES\n");
1207    (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
1208    (void) printf("\t\t%s,\t/* ...corresponding names */\n",
1209		  ((NUM_BOOLEANS(term) != BOOLCOUNT)
1210		   || (NUM_NUMBERS(term) != NUMCOUNT)
1211		   || (NUM_STRINGS(term) != STRCOUNT))
1212		  ? name_initializer("string_ext")
1213		  : "(char **)0");
1214
1215    (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
1216    (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
1217    (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
1218
1219    (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
1220		  NUM_BOOLEANS(term) - BOOLCOUNT);
1221    (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
1222		  NUM_NUMBERS(term) - NUMCOUNT);
1223    (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
1224		  NUM_STRINGS(term) - STRCOUNT);
1225
1226    (void) printf("#endif /* NCURSES_XNAMES */\n");
1227#else
1228    (void) term;
1229#endif /* NCURSES_XNAMES */
1230    (void) printf("\t%s\n", R_CURL);
1231}
1232
1233static int
1234optarg_to_number(void)
1235{
1236    char *temp = 0;
1237    long value = strtol(optarg, &temp, 0);
1238
1239    if (temp == 0 || temp == optarg || *temp != 0) {
1240	fprintf(stderr, "Expected a number, not \"%s\"\n", optarg);
1241	ExitProgram(EXIT_FAILURE);
1242    }
1243    return (int) value;
1244}
1245
1246static char *
1247terminal_env(void)
1248{
1249    char *terminal;
1250
1251    if ((terminal = getenv("TERM")) == 0) {
1252	(void) fprintf(stderr,
1253		       "%s: environment variable TERM not set\n",
1254		       _nc_progname);
1255	exit(EXIT_FAILURE);
1256    }
1257    return terminal;
1258}
1259
1260/***************************************************************************
1261 *
1262 * Main sequence
1263 *
1264 ***************************************************************************/
1265
1266int
1267main(int argc, char *argv[])
1268{
1269    /* Avoid "local data >32k" error with mwcc */
1270    /* Also avoid overflowing smaller stacks on systems like AmigaOS */
1271    path *tfile = 0;
1272    char **tname = 0;
1273    int maxterms;
1274
1275    char **myargv;
1276
1277    char *firstdir, *restdir;
1278    int c, i, len;
1279    bool formatted = FALSE;
1280    bool filecompare = FALSE;
1281    int initdump = 0;
1282    bool init_analyze = FALSE;
1283    bool suppress_untranslatable = FALSE;
1284
1285    if (pledge("stdio rpath", NULL) == -1) {
1286	perror("pledge");
1287	exit(1);
1288    }
1289
1290    /* where is the terminfo database location going to default to? */
1291    restdir = firstdir = 0;
1292
1293#if NCURSES_XNAMES
1294    use_extended_names(FALSE);
1295#endif
1296
1297    _nc_progname = _nc_rootname(argv[0]);
1298
1299    /* make sure we have enough space to add two terminal entries */
1300    myargv = typeCalloc(char *, (size_t) (argc + 3));
1301    memcpy(myargv, argv, (sizeof(char *) * (size_t) argc));
1302    argv = myargv;
1303
1304    while ((c = getopt(argc,
1305		       argv,
1306		       "1A:aB:CcdEeFfGgIiLlnpqR:rs:TtUuVv:w:x")) != -1) {
1307	switch (c) {
1308	case '1':
1309	    mwidth = 0;
1310	    break;
1311
1312	case 'A':
1313	    firstdir = optarg;
1314	    break;
1315
1316#if NCURSES_XNAMES
1317	case 'a':
1318	    _nc_disable_period = TRUE;
1319	    use_extended_names(TRUE);
1320	    break;
1321#endif
1322	case 'B':
1323	    restdir = optarg;
1324	    break;
1325
1326	case 'C':
1327	    outform = F_TERMCAP;
1328	    tversion = "BSD";
1329	    if (sortmode == S_DEFAULT)
1330		sortmode = S_TERMCAP;
1331	    break;
1332
1333	case 'c':
1334	    compare = C_COMMON;
1335	    break;
1336
1337	case 'd':
1338	    compare = C_DIFFERENCE;
1339	    break;
1340
1341	case 'E':
1342	    initdump |= 2;
1343	    break;
1344
1345	case 'e':
1346	    initdump |= 1;
1347	    break;
1348
1349	case 'F':
1350	    filecompare = TRUE;
1351	    break;
1352
1353	case 'f':
1354	    formatted = TRUE;
1355	    break;
1356
1357	case 'G':
1358	    numbers = 1;
1359	    break;
1360
1361	case 'g':
1362	    numbers = -1;
1363	    break;
1364
1365	case 'I':
1366	    outform = F_TERMINFO;
1367	    if (sortmode == S_DEFAULT)
1368		sortmode = S_VARIABLE;
1369	    tversion = 0;
1370	    break;
1371
1372	case 'i':
1373	    init_analyze = TRUE;
1374	    break;
1375
1376	case 'L':
1377	    outform = F_VARIABLE;
1378	    if (sortmode == S_DEFAULT)
1379		sortmode = S_VARIABLE;
1380	    break;
1381
1382	case 'l':
1383	    outform = F_TERMINFO;
1384	    break;
1385
1386	case 'n':
1387	    compare = C_NAND;
1388	    break;
1389
1390	case 'p':
1391	    ignorepads = TRUE;
1392	    break;
1393
1394	case 'q':
1395	    quiet = TRUE;
1396	    s_absent = "-";
1397	    s_cancel = "@";
1398	    bool_sep = ", ";
1399	    break;
1400
1401	case 'R':
1402	    tversion = optarg;
1403	    break;
1404
1405	case 'r':
1406	    tversion = 0;
1407	    break;
1408
1409	case 's':
1410	    if (*optarg == 'd')
1411		sortmode = S_NOSORT;
1412	    else if (*optarg == 'i')
1413		sortmode = S_TERMINFO;
1414	    else if (*optarg == 'l')
1415		sortmode = S_VARIABLE;
1416	    else if (*optarg == 'c')
1417		sortmode = S_TERMCAP;
1418	    else {
1419		(void) fprintf(stderr,
1420			       "%s: unknown sort mode\n",
1421			       _nc_progname);
1422		ExitProgram(EXIT_FAILURE);
1423	    }
1424	    break;
1425
1426	case 'T':
1427	    limited = FALSE;
1428	    break;
1429
1430#if NCURSES_XNAMES
1431	case 't':
1432	    _nc_disable_period = FALSE;
1433	    suppress_untranslatable = TRUE;
1434	    break;
1435#endif
1436
1437	case 'U':
1438	    literal = TRUE;
1439	    break;
1440
1441	case 'u':
1442	    compare = C_USEALL;
1443	    break;
1444
1445	case 'V':
1446	    puts(curses_version());
1447	    ExitProgram(EXIT_SUCCESS);
1448
1449	case 'v':
1450	    itrace = optarg_to_number();
1451	    set_trace_level(itrace);
1452	    break;
1453
1454	case 'w':
1455	    mwidth = optarg_to_number();
1456	    break;
1457
1458#if NCURSES_XNAMES
1459	case 'x':
1460	    use_extended_names(TRUE);
1461	    break;
1462#endif
1463
1464	default:
1465	    usage();
1466	}
1467    }
1468
1469    maxterms = (argc + 2 - optind);
1470    tfile = typeMalloc(path, maxterms);
1471    tname = typeCalloc(char *, maxterms);
1472    entries = typeCalloc(ENTRY, maxterms);
1473
1474    if (tfile == 0
1475	|| tname == 0
1476	|| entries == 0) {
1477	fprintf(stderr, "%s: not enough memory\n", _nc_progname);
1478	ExitProgram(EXIT_FAILURE);
1479    }
1480
1481    /* by default, sort by terminfo name */
1482    if (sortmode == S_DEFAULT)
1483	sortmode = S_TERMINFO;
1484
1485    /* set up for display */
1486    dump_init(tversion, outform, sortmode, mwidth, itrace, formatted);
1487
1488    /* make sure we have at least one terminal name to work with */
1489    if (optind >= argc)
1490	argv[argc++] = terminal_env();
1491
1492    /* if user is after a comparison, make sure we have two entries */
1493    if (compare != C_DEFAULT && optind >= argc - 1)
1494	argv[argc++] = terminal_env();
1495
1496    /* exactly two terminal names with no options means do -d */
1497    if (argc - optind == 2 && compare == C_DEFAULT)
1498	compare = C_DIFFERENCE;
1499
1500    if (!filecompare) {
1501	/* grab the entries */
1502	termcount = 0;
1503	for (; optind < argc; optind++) {
1504	    const char *directory = termcount ? restdir : firstdir;
1505	    int status;
1506
1507	    tname[termcount] = argv[optind];
1508
1509	    if (directory) {
1510#if USE_DATABASE
1511#if MIXEDCASE_FILENAMES
1512#define LEAF_FMT "%c"
1513#else
1514#define LEAF_FMT "%02x"
1515#endif
1516		(void) snprintf(tfile[termcount], sizeof (path),
1517		    	       "%s/" LEAF_FMT "/%s", directory,
1518			       UChar(*argv[optind]), argv[optind]);
1519		if (itrace)
1520		    (void) fprintf(stderr,
1521				   "%s: reading entry %s from file %s\n",
1522				   _nc_progname,
1523				   argv[optind], tfile[termcount]);
1524
1525		status = _nc_read_file_entry(tfile[termcount],
1526					     &entries[termcount].tterm);
1527#else
1528		(void) fprintf(stderr, "%s: terminfo files not supported\n",
1529			       _nc_progname);
1530		ExitProgram(EXIT_FAILURE);
1531#endif
1532	    } else {
1533		if (itrace)
1534		    (void) fprintf(stderr,
1535				   "%s: reading entry %s from database\n",
1536				   _nc_progname,
1537				   tname[termcount]);
1538
1539		status = _nc_read_entry(tname[termcount],
1540					tfile[termcount],
1541					&entries[termcount].tterm);
1542		directory = TERMINFO;	/* for error message */
1543	    }
1544
1545	    if (status <= 0) {
1546		(void) fprintf(stderr,
1547			       "%s: couldn't open terminfo file %s.\n",
1548			       _nc_progname,
1549			       tfile[termcount]);
1550		ExitProgram(EXIT_FAILURE);
1551	    }
1552	    repair_acsc(&entries[termcount].tterm);
1553	    termcount++;
1554	}
1555
1556#if NCURSES_XNAMES
1557	if (termcount > 1)
1558	    _nc_align_termtype(&entries[0].tterm, &entries[1].tterm);
1559#endif
1560
1561	/* dump as C initializer for the terminal type */
1562	if (initdump) {
1563	    if (initdump & 1)
1564		dump_termtype(&entries[0].tterm);
1565	    if (initdump & 2)
1566		dump_initializers(&entries[0].tterm);
1567	}
1568
1569	/* analyze the init strings */
1570	else if (init_analyze) {
1571#undef CUR
1572#define CUR	entries[0].tterm.
1573	    analyze_string("is1", init_1string, &entries[0].tterm);
1574	    analyze_string("is2", init_2string, &entries[0].tterm);
1575	    analyze_string("is3", init_3string, &entries[0].tterm);
1576	    analyze_string("rs1", reset_1string, &entries[0].tterm);
1577	    analyze_string("rs2", reset_2string, &entries[0].tterm);
1578	    analyze_string("rs3", reset_3string, &entries[0].tterm);
1579	    analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
1580	    analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
1581#undef CUR
1582	} else {
1583
1584	    /*
1585	     * Here's where the real work gets done
1586	     */
1587	    switch (compare) {
1588	    case C_DEFAULT:
1589		if (itrace)
1590		    (void) fprintf(stderr,
1591				   "%s: about to dump %s\n",
1592				   _nc_progname,
1593				   tname[0]);
1594		(void) printf("#\tReconstructed via infocmp from file: %s\n",
1595			      tfile[0]);
1596		dump_entry(&entries[0].tterm,
1597			   suppress_untranslatable,
1598			   limited,
1599			   numbers,
1600			   NULL);
1601		len = show_entry();
1602		if (itrace)
1603		    (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len);
1604		break;
1605
1606	    case C_DIFFERENCE:
1607		if (itrace)
1608		    (void) fprintf(stderr, "%s: dumping differences\n", _nc_progname);
1609		(void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1610		compare_entry(compare_predicate, &entries->tterm, quiet);
1611		break;
1612
1613	    case C_COMMON:
1614		if (itrace)
1615		    (void) fprintf(stderr,
1616				   "%s: dumping common capabilities\n",
1617				   _nc_progname);
1618		(void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1619		compare_entry(compare_predicate, &entries->tterm, quiet);
1620		break;
1621
1622	    case C_NAND:
1623		if (itrace)
1624		    (void) fprintf(stderr,
1625				   "%s: dumping differences\n",
1626				   _nc_progname);
1627		(void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1628		compare_entry(compare_predicate, &entries->tterm, quiet);
1629		break;
1630
1631	    case C_USEALL:
1632		if (itrace)
1633		    (void) fprintf(stderr, "%s: dumping use entry\n", _nc_progname);
1634		dump_entry(&entries[0].tterm,
1635			   suppress_untranslatable,
1636			   limited,
1637			   numbers,
1638			   use_predicate);
1639		for (i = 1; i < termcount; i++)
1640		    dump_uses(tname[i], !(outform == F_TERMCAP
1641					  || outform == F_TCONVERR));
1642		len = show_entry();
1643		if (itrace)
1644		    (void) fprintf(stderr, "%s: length %d\n", _nc_progname, len);
1645		break;
1646	    }
1647	}
1648    } else if (compare == C_USEALL)
1649	(void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
1650    else if (compare == C_DEFAULT)
1651	(void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
1652    else if (argc - optind != 2)
1653	(void) fprintf(stderr,
1654		       "File comparison needs exactly two file arguments.\n");
1655    else
1656	file_comparison(argc - optind, argv + optind);
1657
1658#if NO_LEAKS
1659    free(myargv);
1660    free(tfile);
1661    free(tname);
1662#endif
1663    ExitProgram(EXIT_SUCCESS);
1664}
1665
1666/* infocmp.c ends here */
1667