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