ncurses 4.1
[ncurses.git] / progs / infocmp.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22
23 /*
24  *      infocmp.c -- decompile an entry, or compare two entries
25  *              written by Eric S. Raymond
26  */
27
28 #include <progs.priv.h>
29
30 #include <ctype.h>
31
32 #include <term_entry.h>
33 #include <dump_entry.h>
34
35 MODULE_ID("$Id: infocmp.c,v 1.27 1997/02/15 18:54:44 tom Exp $")
36
37 #define L_CURL "{"
38 #define R_CURL "}"
39
40 #define VALID_STRING(s) ((s) != CANCELLED_STRING && (s) != ABSENT_STRING)
41
42 #define MAXTERMS        32      /* max # terminal arguments we can handle */
43
44 const char *_nc_progname = "infocmp";
45
46 typedef char    path[PATH_MAX];
47
48 /***************************************************************************
49  *
50  * The following control variables, together with the contents of the
51  * terminfo entries, completely determine the actions of the program.
52  *
53  ***************************************************************************/
54
55 static char *tname[MAXTERMS];   /* terminal type names */
56 static TERMTYPE term[MAXTERMS]; /* terminfo entries */
57 static int termcount;           /* count of terminal entries */
58
59 static const char *tversion;    /* terminfo version selected */
60 static int outform;             /* output format */
61 static int sortmode;            /* sort_mode */
62 static int itrace;              /* trace flag for debugging */
63 static int mwidth = 60;
64
65 /* main comparison mode */
66 static int compare;
67 #define C_DEFAULT       0       /* don't force comparison mode */
68 #define C_DIFFERENCE    1       /* list differences between two terminals */
69 #define C_COMMON        2       /* list common capabilities */
70 #define C_NAND          3       /* list capabilities in neither terminal */
71 #define C_USEALL        4       /* generate relative use-form entry */
72 static bool ignorepads;         /* ignore pad prefixes when diffing */
73
74 #if NO_LEAKS
75 #undef ExitProgram
76 static void ExitProgram(int code) GCC_NORETURN;
77 static void ExitProgram(int code)
78 {
79         while (termcount-- > 0)
80                 _nc_free_termtype(&term[termcount], FALSE);
81         _nc_leaks_dump_entry();
82         _nc_free_and_exit(code);
83 }
84 #endif
85
86 static char *canonical_name(char *ptr, char *buf)
87 /* extract the terminal type's primary name */
88 {
89     char        *bp;
90
91     (void) strcpy(buf, ptr);
92     if ((bp = strchr(buf, '|')) != (char *)NULL)
93         *bp = '\0';
94
95     return(buf);
96 }
97
98 /***************************************************************************
99  *
100  * Predicates for dump function
101  *
102  ***************************************************************************/
103
104 static int capcmp(const char *s, const char *t)
105 /* capability comparison function */
106 {
107     if (!VALID_STRING(s) && !VALID_STRING(t))
108         return(0);
109     else if (!VALID_STRING(s) || !VALID_STRING(t))
110         return(1);
111
112     if (ignorepads)
113         return(_nc_capcmp(s, t));
114     else
115         return(strcmp(s, t));
116 }
117
118 static int use_predicate(int type, int idx)
119 /* predicate function to use for use decompilation */
120 {
121         TERMTYPE *tp;
122
123         switch(type)
124         {
125         case BOOLEAN: {
126                 int is_set = FALSE;
127
128                 /*
129                  * This assumes that multiple use entries are supposed
130                  * to contribute the logical or of their boolean capabilities.
131                  * This is true if we take the semantics of multiple uses to
132                  * be 'each capability gets the first non-default value found
133                  * in the sequence of use entries'.
134                  */
135                 for (tp = &term[1]; tp < term + termcount; tp++)
136                         if (tp->Booleans[idx]) {
137                                 is_set = TRUE;
138                                 break;
139                         }
140                         if (is_set != term->Booleans[idx])
141                                 return(!is_set);
142                         else
143                                 return(FAIL);
144                 }
145
146         case NUMBER: {
147                 int     value = ABSENT_NUMERIC;
148
149                 /*
150                  * We take the semantics of multiple uses to be 'each
151                  * capability gets the first non-default value found
152                  * in the sequence of use entries'.
153                  */
154                 for (tp = &term[1]; tp < term + termcount; tp++)
155                         if (tp->Numbers[idx] >= 0) {
156                                 value = tp->Numbers[idx];
157                                 break;
158                         }
159
160                 if (value != term->Numbers[idx])
161                         return(value != ABSENT_NUMERIC);
162                 else
163                         return(FAIL);
164                 }
165
166         case STRING: {
167                 char *termstr, *usestr = ABSENT_STRING;
168
169                 termstr = term->Strings[idx];
170
171                 /*
172                  * We take the semantics of multiple uses to be 'each
173                  * capability gets the first non-default value found
174                  * in the sequence of use entries'.
175                  */
176                 for (tp = &term[1]; tp < term + termcount; tp++)
177                         if (tp->Strings[idx])
178                         {
179                                 usestr = tp->Strings[idx];
180                                 break;
181                         }
182
183                 if (usestr == ABSENT_STRING && termstr == ABSENT_STRING)
184                         return(FAIL);
185                 else if (!usestr || !termstr || capcmp(usestr,termstr))
186                         return(TRUE);
187                 else
188                         return(FAIL);
189             }
190         }
191
192         return(FALSE);  /* pacify compiler */
193 }
194
195 static bool entryeq(TERMTYPE *t1, TERMTYPE *t2)
196 /* are two terminal types equal */
197 {
198     int i;
199
200     for (i = 0; i < BOOLCOUNT; i++)
201         if (t1->Booleans[i] != t2->Booleans[i])
202             return(FALSE);
203
204     for (i = 0; i < NUMCOUNT; i++)
205         if (t1->Numbers[i] != t2->Numbers[i])
206             return(FALSE);
207
208     for (i = 0; i < STRCOUNT; i++)
209         if (capcmp(t1->Strings[i], t2->Strings[i]))
210             return(FALSE);
211
212     return(TRUE);
213 }
214
215 static void compare_predicate(int type, int idx, const char *name)
216 /* predicate function to use for entry difference reports */
217 {
218         register TERMTYPE *t1 = &term[0];
219         register TERMTYPE *t2 = &term[1];
220         char *s1, *s2;
221
222         switch(type)
223         {
224         case BOOLEAN:
225                 switch(compare)
226                 {
227                 case C_DIFFERENCE:
228                         if (t1->Booleans[idx] != t2->Booleans[idx])
229                         (void) printf("\t%s: %c:%c.\n",
230                                           name,
231                                           t1->Booleans[idx] ? 'T' : 'F',
232                                           t2->Booleans[idx] ? 'T' : 'F');
233                         break;
234
235                 case C_COMMON:
236                         if (t1->Booleans[idx] && t2->Booleans[idx])
237                         (void) printf("\t%s= T.\n", name);
238                         break;
239
240                 case C_NAND:
241                         if (!t1->Booleans[idx] && !t2->Booleans[idx])
242                         (void) printf("\t!%s.\n", name);
243                         break;
244                 }
245                 break;
246
247         case NUMBER:
248                 switch(compare)
249                 {
250                 case C_DIFFERENCE:
251                         if (t1->Numbers[idx] != t2->Numbers[idx])
252                         (void) printf("\t%s: %d:%d.\n",
253                                           name, t1->Numbers[idx], t2->Numbers[idx]);
254                         break;
255
256                 case C_COMMON:
257                         if (t1->Numbers[idx]!=-1 && t2->Numbers[idx]!=-1
258                                 && t1->Numbers[idx] == t2->Numbers[idx])
259                         (void) printf("\t%s= %d.\n", name, t1->Numbers[idx]);
260                         break;
261
262                 case C_NAND:
263                         if (t1->Numbers[idx]==-1 && t2->Numbers[idx] == -1)
264                         (void) printf("\t!%s.\n", name);
265                         break;
266                 }
267         break;
268
269         case STRING:
270                 s1 = t1->Strings[idx];
271                 s2 = t2->Strings[idx];
272                 switch(compare)
273                 {
274                 case C_DIFFERENCE:
275                         if (capcmp(s1, s2))
276                         {
277                                 char    buf1[BUFSIZ], buf2[BUFSIZ];
278
279                                 if (s1 == (char *)NULL)
280                                         (void) strcpy(buf1, "NULL");
281                                 else
282                                 {
283                                         (void) strcpy(buf1, "'");
284                                         (void) strcat(buf1, expand(s1));
285                                         (void) strcat(buf1, "'");
286                                 }
287
288                                 if (s2 == (char *)NULL)
289                                         (void) strcpy(buf2, "NULL");
290                                 else
291                                 {
292                                         (void) strcpy(buf2, "'");
293                                         (void) strcat(buf2, expand(s2));
294                                         (void) strcat(buf2, "'");
295                                 }
296
297                                 (void) printf("\t%s: %s, %s.\n",
298                                               name, buf1, buf2);
299                         }
300                         break;
301
302                 case C_COMMON:
303                         if (s1 && s2 && !capcmp(s1, s2))
304                                 (void) printf("\t%s= '%s'.\n",name,expand(s1));
305                         break;
306
307                 case C_NAND:
308                         if (!s1 && !s2)
309                                 (void) printf("\t!%s.\n", name);
310                         break;
311                 }
312                 break;
313         }
314
315 }
316
317 /***************************************************************************
318  *
319  * Init string analysis
320  *
321  ***************************************************************************/
322
323 typedef struct {const char *from; const char *to;} assoc;
324
325 static const assoc std_caps[] =
326 {
327     /* these are specified by X.364 and iBCS2 */
328     {"\033c",   "RIS"},         /* full reset */
329     {"\0337",   "SC"},          /* save cursor */
330     {"\0338",   "RC"},          /* restore cursor */
331     {"\033[r",  "RSR"},         /* not an X.364 mnemonic */
332     {"\033[m",  "SGR0"},        /* not an X.364 mnemonic */
333     {"\033[2J", "ED2"},         /* clear page */
334
335     /* this group is specified by ISO 2022 */
336     {"\033(0",  "ISO DEC G0"},  /* enable DEC graphics for G0 */
337     {"\033(A",  "ISO UK G0"},   /* enable UK chars for G0 */
338     {"\033(B",  "ISO US G0"},   /* enable US chars for G0 */
339     {"\033)0",  "ISO DEC G1"},  /* enable DEC graphics for G1 */
340     {"\033)A",  "ISO UK G1"},   /* enable UK chars for G1 */
341     {"\033)B",  "ISO US G1"},   /* enable US chars for G1 */
342
343     /* these are DEC private modes widely supported by emulators */
344     {"\033=",   "DECPAM"},      /* application keypad mode */
345     {"\033>",   "DECPNM"},      /* normal keypad mode */
346     {"\033<",   "DECANSI"},     /* enter ANSI mode */
347
348     { (char *)0, (char *)0}
349 };
350
351 static const assoc private_modes[] =
352 /* DEC \E[ ... [hl] modes recognized by many emulators */
353 {
354     {"1",       "CKM"},         /* application cursor keys */
355     {"2",       "ANM"},         /* set VT52 mode */
356     {"3",       "COLM"},        /* 132-column mode */
357     {"4",       "SCLM"},        /* smooth scroll */
358     {"5",       "SCNM"},        /* reverse video mode */
359     {"6",       "OM"},          /* origin mode */
360     {"7",       "AWM"},         /* wraparound mode */
361     {"8",       "ARM"},         /* auto-repeat mode */
362     {(char *)0, (char *)0}
363 };
364
365 static const assoc ecma_highlights[] =
366 /* recognize ECMA attribute sequences */
367 {
368     {"0",       "NORMAL"},      /* normal */
369     {"1",       "+BOLD"},       /* bold on */
370     {"2",       "+DIM"},        /* dim on */
371     {"3",       "+ITALIC"},     /* italic on */
372     {"4",       "+UNDERLINE"},  /* underline on */
373     {"5",       "+BLINK"},      /* blink on */
374     {"6",       "+FASTBLINK"},  /* fastblink on */
375     {"7",       "+REVERSE"},    /* reverse on */
376     {"8",       "+INVISIBLE"},  /* invisible on */
377     {"9",       "+DELETED"},    /* deleted on */
378     {"10",      "MAIN-FONT"},   /* select primary font */
379     {"11",      "ALT-FONT-1"},  /* select alternate font 1 */
380     {"12",      "ALT-FONT-2"},  /* select alternate font 2 */
381     {"13",      "ALT-FONT-3"},  /* select alternate font 3 */
382     {"14",      "ALT-FONT-4"},  /* select alternate font 4 */
383     {"15",      "ALT-FONT-5"},  /* select alternate font 5 */
384     {"16",      "ALT-FONT-6"},  /* select alternate font 6 */
385     {"17",      "ALT-FONT-7"},  /* select alternate font 7 */
386     {"18",      "ALT-FONT-1"},  /* select alternate font 1 */
387     {"19",      "ALT-FONT-1"},  /* select alternate font 1 */
388     {"20",      "FRAKTUR"},     /* Fraktur font */
389     {"21",      "DOUBLEUNDER"}, /* double underline */
390     {"22",      "-DIM"},        /* dim off */
391     {"23",      "-ITALIC"},     /* italic off */
392     {"24",      "-UNDERLINE"},  /* underline off */
393     {"25",      "-BLINK"},      /* blink off */
394     {"26",      "-FASTBLINK"},  /* fastblink off */
395     {"27",      "-REVERSE"},    /* reverse off */
396     {"28",      "-INVISIBLE"},  /* invisible off */
397     {"29",      "-DELETED"},    /* deleted off */
398     {(char *)0, (char *)0}
399 };
400
401 static void analyze_string(const char *name, const char *cap, TERMTYPE *tp)
402 {
403     char        buf[MAX_TERMINFO_LENGTH];
404     char        buf2[MAX_TERMINFO_LENGTH];
405     const char  *sp, *ep;
406     const assoc *ap;
407
408     if (cap == ABSENT_STRING || cap == CANCELLED_STRING)
409         return;
410     (void) printf("%s: ", name);
411
412     buf[0] = '\0';
413     for (sp = cap; *sp; sp++)
414     {
415         int     i;
416         size_t  len = 0;
417         const char *expansion = 0;
418
419         /* first, check other capabilities in this entry */
420         for (i = 0; i < STRCOUNT; i++)
421         {
422             char        *cp = tp->Strings[i];
423
424             /* don't use soft-key capabilities */
425             if (strnames[i][0] == 'k' && strnames[i][0] == 'f')
426                 continue;
427
428
429             if (cp != ABSENT_STRING && cp != CANCELLED_STRING && cp[0] && cp != cap)
430             {
431                 len = strlen(cp);
432                 (void) strncpy(buf2, sp, len);
433                 buf2[len] = '\0';
434
435                 if (_nc_capcmp(cp, buf2))
436                     continue;
437
438 #define ISRS(s) (!strncmp((s), "is", 2) || !strncmp((s), "rs", 2))
439                 /*
440                  * Theoretically we just passed the test for translation
441                  * (equality once the padding is stripped).  However, there
442                  * are a few more hoops that need to be jumped so that
443                  * identical pairs of initialization and reset strings
444                  * don't just refer to each other.
445                  */
446                 if (ISRS(name) || ISRS(strnames[i]))
447                     if (cap < cp)
448                         continue;
449 #undef ISRS
450
451                 expansion = strnames[i];
452                 break;
453             }
454         }
455
456         /* now check the standard capabilities */
457         if (!expansion)
458             for (ap = std_caps; ap->from; ap++)
459             {
460                 len = strlen(ap->from);
461
462                 if (strncmp(ap->from, sp, len) == 0)
463                 {
464                     expansion = ap->to;
465                     break;
466                 }
467             }
468
469         /* now check for private-mode sequences */
470         if (!expansion
471                     && sp[0] == '\033' && sp[1] == '[' && sp[2] == '?'
472                     && (len = strspn(sp + 3, "0123456789;"))
473                     && ((sp[3 + len] == 'h') || (sp[3 + len] == 'l')))
474         {
475             char        buf3[MAX_TERMINFO_LENGTH];
476
477             (void) strcpy(buf2, (sp[3 + len] == 'h') ? "DEC+" : "DEC-");
478             (void) strncpy(buf3, sp + 3, len);
479             len += 4;
480             buf3[len] = '\0';
481
482             ep = strtok(buf3, ";");
483             do {
484                    bool found = FALSE;
485
486                    for (ap = private_modes; ap->from; ap++)
487                    {
488                        size_t tlen = strlen(ap->from);
489
490                        if (strncmp(ap->from, ep, tlen) == 0)
491                        {
492                            (void) strcat(buf2, ap->to);
493                            found = TRUE;
494                            break;
495                        }
496                    }
497
498                    if (!found)
499                        (void) strcat(buf2, ep);
500                    (void) strcat(buf2, ";");
501                } while
502                    ((ep = strtok((char *)NULL, ";")));
503             buf2[strlen(buf2) - 1] = '\0';
504             expansion = buf2;
505         }
506
507         /* now check for ECMA highlight sequences */
508         if (!expansion
509                     && sp[0] == '\033' && sp[1] == '['
510                     && (len = strspn(sp + 2, "0123456789;"))
511                     && sp[2 + len] == 'm')
512         {
513             char        buf3[MAX_TERMINFO_LENGTH];
514
515             (void) strcpy(buf2, "SGR:");
516             (void) strncpy(buf3, sp + 2, len);
517             len += 3;
518             buf3[len] = '\0';
519
520             ep = strtok(buf3, ";");
521             do {
522                    bool found = FALSE;
523
524                    for (ap = ecma_highlights; ap->from; ap++)
525                    {
526                        size_t tlen = strlen(ap->from);
527
528                        if (strncmp(ap->from, ep, tlen) == 0)
529                        {
530                            (void) strcat(buf2, ap->to);
531                            found = TRUE;
532                            break;
533                        }
534                    }
535
536                    if (!found)
537                        (void) strcat(buf2, ep);
538                    (void) strcat(buf2, ";");
539                } while
540                    ((ep = strtok((char *)NULL, ";")));
541
542             buf2[strlen(buf2) - 1] = '\0';
543             expansion = buf2;
544         }
545         /* now check for scroll region reset */
546         if (!expansion)
547         {
548             (void) sprintf(buf2, "\033[1;%dr", tp->Numbers[2]);
549             len = strlen(buf2);
550             if (strncmp(buf2, sp, len) == 0)
551                 expansion = "RSR";
552         }
553
554         /* now check for home-down */
555         if (!expansion)
556         {
557             (void) sprintf(buf2, "\033[%d;1H", tp->Numbers[2]);
558             len = strlen(buf2);
559             if (strncmp(buf2, sp, len) == 0)
560                     expansion = "LL";
561         }
562
563         /* now look at the expansion we got, if any */
564         if (expansion)
565         {
566             (void) sprintf(buf + strlen(buf), "{%s}", expansion);
567             sp += len - 1;
568             continue;
569         }
570         else
571         {
572             /* couldn't match anything */
573             buf2[0] = *sp;
574             buf2[1] = '\0';
575             (void) strcat(buf, expand(buf2));
576         }
577     }
578     (void) printf("%s\n", buf);
579 }
580
581 /***************************************************************************
582  *
583  * File comparison
584  *
585  ***************************************************************************/
586
587 static void file_comparison(int argc, char *argv[])
588 {
589 #define MAXCOMPARE      2
590     /* someday we may allow comparisons on more files */
591     int filecount = 0;
592     ENTRY       *heads[MAXCOMPARE];
593     ENTRY       *tails[MAXCOMPARE];
594     ENTRY       *qp, *rp;
595     int         i, n;
596
597     dump_init((char *)NULL, F_LITERAL, S_TERMINFO, 0, itrace);
598
599     for (n = 0; n < argc && n < MAXCOMPARE; n++)
600     {
601         if (freopen(argv[n], "r", stdin) == NULL)
602             _nc_err_abort("Can't open %s", argv[n]);
603
604         _nc_head = _nc_tail = (ENTRY *)NULL;
605
606         /* parse entries out of the source file */
607         _nc_set_source(argv[n]);
608         _nc_read_entry_source(stdin, NULL, TRUE, FALSE, NULLHOOK);
609
610         if (itrace)
611             (void) fprintf(stderr, "Resolving file %d...\n",n-0);
612
613         /* do use resolution */
614         if (!_nc_resolve_uses())
615         {
616             (void) fprintf(stderr,
617                            "There are unresolved use entries in %s:\n",
618                            argv[n]);
619             for_entry_list(qp)
620                 if (qp->nuses)
621                 {
622                     (void) fputs(qp->tterm.term_names, stderr);
623                     (void) fputc('\n', stderr);
624                 }
625             exit(EXIT_FAILURE);
626         }
627
628         heads[filecount] = _nc_head;
629         tails[filecount] = _nc_tail;
630         filecount++;
631     }
632
633     /* OK, all entries are in core.  Ready to do the comparison */
634     if (itrace)
635         (void) fprintf(stderr, "Entries are now in core...\n");
636
637     /*
638      * The entry-matching loop.  We're not using the use[]
639      * slots any more (they got zeroed out by resolve_uses) so
640      * we stash each entry's matches in the other file there.
641      * Sigh, this is intrinsically quadratic.
642      */
643     for (qp = heads[0]; qp; qp = qp->next)
644     {
645         for (rp = heads[1]; rp; rp = rp->next)
646             if (_nc_entry_match(qp->tterm.term_names,rp->tterm.term_names))
647             {
648                 /*
649                  * This is why the uses structure parent element is
650                  * (void *) -- so we can have either (char *) for
651                  * names or entry structure pointers in them and still
652                  * be type-safe.
653                  */
654                 if (qp->nuses < MAX_USES)
655                     qp->uses[qp->nuses].parent = (void *)rp;
656                 qp->nuses++;
657
658                 if (rp->nuses < MAX_USES)
659                     rp->uses[rp->nuses].parent = (void *)qp;
660                 rp->nuses++;
661             }
662     }
663
664     /* now we have two circular lists with crosslinks */
665     if (itrace)
666         (void) fprintf(stderr, "Name matches are done...\n");
667
668     for (qp = heads[0]; qp; qp = qp->next)
669         if (qp->nuses > 1)
670         {
671             (void) fprintf(stderr,
672                            "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
673                            _nc_first_name(qp->tterm.term_names),
674                            argv[0],
675                            qp->nuses,
676                            argv[1]);
677             for (i = 0; i < qp->nuses; i++)
678                 (void) fprintf(stderr,
679                                "\t%s\n",
680                                _nc_first_name(((ENTRY *)qp->uses[i].parent)->tterm.term_names));
681         }
682     for (rp = heads[1]; rp; rp = rp->next)
683         if (rp->nuses > 1)
684         {
685             (void) fprintf(stderr,
686                            "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
687                            _nc_first_name(rp->tterm.term_names),
688                            argv[1],
689                            rp->nuses,
690                            argv[0]);
691             for (i = 0; i < rp->nuses; i++)
692                 (void) fprintf(stderr,
693                                "\t%s\n",
694                                _nc_first_name(((ENTRY *)rp->uses[i].parent)->tterm.term_names));
695         }
696
697     (void) printf("In file 1 (%s) only:\n", argv[0]);
698     for (qp = heads[0]; qp; qp = qp->next)
699         if (qp->nuses == 0)
700             (void) printf("\t%s\n",
701                           _nc_first_name(qp->tterm.term_names));
702
703     (void) printf("In file 2 (%s) only:\n", argv[1]);
704     for (rp = heads[1]; rp; rp = rp->next)
705         if (rp->nuses == 0)
706             (void) printf("\t%s\n",
707                           _nc_first_name(rp->tterm.term_names));
708
709     (void) printf("The following entries are equivalent:\n");
710     for (qp = heads[0]; qp; qp = qp->next)
711     {
712         rp = (ENTRY *)qp->uses[0].parent;
713
714         if (qp->nuses == 1 && entryeq(&qp->tterm, &rp->tterm))
715         {
716             char name1[NAMESIZE], name2[NAMESIZE];
717
718             (void) canonical_name(qp->tterm.term_names, name1);
719             (void) canonical_name(rp->tterm.term_names, name2);
720
721             (void) printf("%s = %s\n", name1, name2);
722         }
723     }
724
725     (void) printf("Differing entries:\n");
726     termcount = 2;
727     for (qp = heads[0]; qp; qp = qp->next)
728     {
729         rp = (ENTRY *)qp->uses[0].parent;
730
731         if (qp->nuses == 1 && !entryeq(&qp->tterm, &rp->tterm))
732         {
733             char name1[NAMESIZE], name2[NAMESIZE];
734
735             memcpy(&term[0], &qp->tterm, sizeof(TERMTYPE));
736             memcpy(&term[1], &rp->tterm, sizeof(TERMTYPE));
737
738             (void) canonical_name(qp->tterm.term_names, name1);
739             (void) canonical_name(rp->tterm.term_names, name2);
740
741             switch (compare)
742             {
743             case C_DIFFERENCE:
744                 if (itrace)
745                     (void)fprintf(stderr,"infocmp: dumping differences\n");
746                 (void) printf("comparing %s to %s.\n", name1, name2);
747                 compare_entry(compare_predicate);
748                 break;
749
750             case C_COMMON:
751                 if (itrace)
752                     (void) fprintf(stderr,
753                                    "infocmp: dumping common capabilities\n");
754                 (void) printf("comparing %s to %s.\n", name1, name2);
755                 compare_entry(compare_predicate);
756                 break;
757
758             case C_NAND:
759                 if (itrace)
760                     (void) fprintf(stderr,
761                                    "infocmp: dumping differences\n");
762                 (void) printf("comparing %s to %s.\n", name1, name2);
763                 compare_entry(compare_predicate);
764                 break;
765
766             }
767         }
768     }
769 }
770
771 static void usage(void)
772 {
773         fprintf(stderr,
774 "usage: infocmp [-dcnILCuvV1T] [-s d| i| l| c] [-w width] [-A directory] [-B directory] [termname...]\n");
775         exit(EXIT_FAILURE);
776 }
777
778 /***************************************************************************
779  *
780  * Main sequence
781  *
782  ***************************************************************************/
783
784 int main(int argc, char *argv[])
785 {
786         char *terminal, *firstdir, *restdir;
787         path tfile[MAXTERMS];
788         int c, i, len;
789         bool filecompare = FALSE;
790         bool initdump = FALSE;
791         bool init_analyze = FALSE;
792         bool limited = TRUE;
793
794         if ((terminal = getenv("TERM")) == NULL)
795         {
796                 (void) fprintf(stderr,
797                         "infocmp: environment variable TERM not set\n");
798                 return EXIT_FAILURE;
799         }
800
801         /* where is the terminfo database location going to default to? */
802         restdir = firstdir = 0;
803
804         while ((c = getopt(argc, argv, "decCFIinlLprR:s:uv:Vw:A:B:1T")) != EOF)
805                 switch (c)
806                 {
807                 case 'd':
808                         compare = C_DIFFERENCE;
809                         break;
810
811                 case 'e':
812                         initdump = TRUE;
813                         break;
814
815                 case 'c':
816                         compare = C_COMMON;
817                         break;
818
819                 case 'C':
820                         outform = F_TERMCAP;
821                         tversion = "BSD";
822                         if (sortmode == S_DEFAULT)
823                             sortmode = S_TERMCAP;
824                         break;
825
826                 case 'F':
827                         filecompare = TRUE;
828                         break;
829
830                 case 'i':
831                         init_analyze = TRUE;
832                         break;
833
834                 case 'l':
835                         outform = F_TERMINFO;
836                         break;
837
838                 case 'L':
839                         outform = F_VARIABLE;
840                         if (sortmode == S_DEFAULT)
841                             sortmode = S_VARIABLE;
842                         break;
843
844                 case 'n':
845                         compare = C_NAND;
846                         break;
847
848                 case 'p':
849                         ignorepads = TRUE;
850                         break;
851
852                 case 'r':
853                         tversion = (char *)NULL;
854                         break;
855
856                 case 'R':
857                         tversion = optarg;
858                         break;
859
860                 case 's':
861                         if (*optarg == 'd')
862                                 sortmode = S_NOSORT;
863                         else if (*optarg == 'i')
864                                 sortmode = S_TERMINFO;
865                         else if (*optarg == 'l')
866                                 sortmode = S_VARIABLE;
867                         else if (*optarg == 'c')
868                                 sortmode = S_TERMCAP;
869                         else
870                         {
871                                 (void) fprintf(stderr,
872                                                "infocmp: unknown sort mode\n");
873                                 return EXIT_FAILURE;
874                         }
875                         break;
876
877                 case 'u':
878                         compare = C_USEALL;
879                         break;
880
881                 case 'v':
882                         itrace = atoi(optarg);
883                         _nc_tracing = (1 << itrace) - 1;
884                         break;
885
886                 case 'V':
887                         (void) fputs(NCURSES_VERSION, stdout);
888                         putchar('\n');
889                         ExitProgram(EXIT_SUCCESS);
890
891                 case 'w':
892                         mwidth = atoi(optarg);
893                         break;
894
895                 case 'A':
896                         firstdir = optarg;
897                         break;
898
899                 case 'B':
900                         restdir = optarg;
901                         break;
902
903                 case '1':
904                         mwidth = 0;
905                         break;
906                 case 'T':
907                         limited = FALSE;
908                         break;
909                 default:
910                         usage();
911                 }
912
913         /* by default, sort by terminfo name */
914         if (sortmode == S_DEFAULT)
915                 sortmode = S_TERMINFO;
916
917         /* set up for display */
918         dump_init(tversion, outform, sortmode, mwidth, itrace);
919
920         /* make sure we have at least one terminal name to work with */
921         if (optind >= argc)
922                 argv[argc++] = terminal;
923
924         /* if user is after a comparison, make sure we have two entries */
925         if (compare != C_DEFAULT && optind >= argc - 1)
926                 argv[argc++] = terminal;
927
928         /* exactly two terminal names with no options means do -d */
929         if (argc - optind == 2 && compare == C_DEFAULT)
930                 compare = C_DIFFERENCE;
931
932         if (!filecompare)
933         {
934             /* grab the entries */
935             termcount = 0;
936             for (; optind < argc; optind++)
937             {
938                 if (termcount >= MAXTERMS)
939                 {
940                     (void) fprintf(stderr,
941                            "infocmp: too many terminal type arguments\n");
942                     return EXIT_FAILURE;
943                 }
944                 else
945                 {
946                     const char  *directory = termcount ? restdir : firstdir;
947                     int         status;
948
949                     tname[termcount] = argv[optind];
950
951                     if (directory)
952                     {
953                         (void) sprintf(tfile[termcount], "%s/%c/%s",
954                                        directory,
955                                        *argv[optind], argv[optind]);
956                         if (itrace)
957                             (void) fprintf(stderr,
958                                            "infocmp: reading entry %s from file %s\n",
959                                            argv[optind], tfile[termcount]);
960
961                         status = _nc_read_file_entry(tfile[termcount],
962                                                      &term[termcount]);
963                     }
964                     else
965                     {
966                         if (itrace)
967                             (void) fprintf(stderr,
968                                            "infocmp: reading entry %s from system directories %s\n",
969                                            argv[optind], tname[termcount]);
970
971                         status = _nc_read_entry(tname[termcount],
972                                                 tfile[termcount],
973                                                 &term[termcount]);
974                         directory = TERMINFO;   /* for error message */
975                     }
976
977                     if (status <= 0)
978                     {
979                         (void) fprintf(stderr,
980                                        "infocmp: couldn't open terminfo file %s.\n",
981                                        tfile[termcount]);
982                         return EXIT_FAILURE;
983                     }
984                     termcount++;
985                 }
986             }
987
988             /* dump as C initializer for the terminal type */
989             if (initdump)
990             {
991                 int     n;
992                 const char *str = 0;
993                 int     size;
994
995                 (void) printf("\t%s\n\t\t\"%s\",\n",
996                               L_CURL, term->term_names);
997                 (void) printf("\t\t(char *)0,\n");
998
999                 (void) printf("\t\t%s /* BOOLEANS */\n", L_CURL);
1000                 for (n = 0; n < BOOLCOUNT; n++)
1001                 {
1002                     switch((int)(term->Booleans[n]))
1003                     {
1004                     case TRUE:
1005                         str = "TRUE";
1006                         break;
1007
1008                     case FALSE:
1009                         str = "FALSE";
1010                         break;
1011
1012                     case ABSENT_BOOLEAN:
1013                         str = "ABSENT_BOOLEAN";
1014                         break;
1015
1016                     case CANCELLED_BOOLEAN:
1017                         str = "CANCELLED_BOOLEAN";
1018                         break;
1019                     }
1020                     (void) printf("\t\t/* %s */\t%s%s,\n",
1021                                   boolnames[n], str,
1022                                   n == BOOLCOUNT-1 ? R_CURL : "");
1023                 }
1024
1025                 (void) printf("\t\t%s /* NUMERICS */\n", L_CURL);
1026                 for (n = 0; n < NUMCOUNT; n++)
1027                 {
1028                     char        buf[BUFSIZ];
1029                     switch (term->Numbers[n])
1030                     {
1031                     case ABSENT_NUMERIC:
1032                         str = "ABSENT_NUMERIC";
1033                         break;
1034                     case CANCELLED_NUMERIC:
1035                         str = "CANCELLED_NUMERIC";
1036                         break;
1037                     default:
1038                         sprintf(buf, "%d", term->Numbers[n]);
1039                         str = buf;
1040                         break;
1041                     }
1042                     (void) printf("\t\t/* %s */\t%s%s,\n",
1043                         numnames[n], str,
1044                         n == NUMCOUNT-1 ? R_CURL : "");
1045                 }
1046
1047                 size = sizeof(TERMTYPE)
1048                     + (BOOLCOUNT * sizeof(term->Booleans[0]))
1049                     + (NUMCOUNT * sizeof(term->Numbers[0]));
1050
1051                 (void) printf("\t\t%s /* STRINGS */\n", L_CURL);
1052                 for (n = 0; n < STRCOUNT; n++)
1053                 {
1054                     char        buf[BUFSIZ], *sp, *tp;
1055
1056                     if (term->Strings[n] == ABSENT_STRING)
1057                         str = "ABSENT_STRING";
1058                     else if (term->Strings[n] == CANCELLED_STRING)
1059                         str = "CANCELLED_STRING";
1060                     else
1061                     {
1062                         tp = buf;
1063                         *tp++ = '"';
1064                         for (sp = term->Strings[n]; *sp; sp++)
1065                             if (isascii(*sp) && isprint(*sp) && *sp !='\\' && *sp != '"')
1066                                 *tp++ = *sp;
1067                             else
1068                             {
1069                                 (void) sprintf(tp, "\\%03o", *sp);
1070                                 tp += 4;
1071                             }
1072                         *tp++ = '"';
1073                         *tp = '\0';
1074                         size += (strlen(term->Strings[n]) + 1);
1075                         str = buf;
1076                     }
1077                     (void) printf("\t\t/* %s */\t%s%s%s\n",
1078                         strnames[n], str,
1079                         n == STRCOUNT-1 ? R_CURL : "",
1080                         n == STRCOUNT-1 ? ""     : ",");
1081                 }
1082                 (void) printf("\t%s /* size = %d */\n", R_CURL, size);
1083                 ExitProgram(EXIT_SUCCESS);
1084             }
1085
1086             /* analyze the init strings */
1087             if (init_analyze)
1088             {
1089 #undef CUR
1090 #define CUR     term[0].
1091                 analyze_string("is1", init_1string, &term[0]);
1092                 analyze_string("is2", init_2string, &term[0]);
1093                 analyze_string("is3", init_3string, &term[0]);
1094                 analyze_string("rs1", reset_1string, &term[0]);
1095                 analyze_string("rs2", reset_2string, &term[0]);
1096                 analyze_string("rs3", reset_3string, &term[0]);
1097                 analyze_string("smcup", enter_ca_mode, &term[0]);
1098                 analyze_string("rmcup", exit_ca_mode, &term[0]);
1099 #undef CUR
1100                 ExitProgram(EXIT_SUCCESS);
1101             }
1102
1103             /*
1104              * Here's where the real work gets done
1105              */
1106             switch (compare)
1107             {
1108             case C_DEFAULT:
1109                 if (itrace)
1110                     (void) fprintf(stderr,
1111                                    "infocmp: about to dump %s\n",
1112                                    tname[0]);
1113                 (void) printf("#\tReconstructed via infocmp from file: %s\n",
1114                               tfile[0]);
1115                 len = dump_entry(&term[0], limited, NULL);
1116                 putchar('\n');
1117                 if (itrace)
1118                     (void)fprintf(stderr,"infocmp: length %d\n", len);
1119                 break;
1120
1121             case C_DIFFERENCE:
1122                 if (itrace)
1123                     (void)fprintf(stderr,"infocmp: dumping differences\n");
1124                 (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1125                 compare_entry(compare_predicate);
1126                 break;
1127
1128             case C_COMMON:
1129                 if (itrace)
1130                     (void) fprintf(stderr,
1131                                    "infocmp: dumping common capabilities\n");
1132                 (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1133                 compare_entry(compare_predicate);
1134                 break;
1135
1136             case C_NAND:
1137                 if (itrace)
1138                     (void) fprintf(stderr,
1139                                    "infocmp: dumping differences\n");
1140                 (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
1141                 compare_entry(compare_predicate);
1142                 break;
1143
1144             case C_USEALL:
1145                 if (itrace)
1146                     (void) fprintf(stderr, "infocmp: dumping use entry\n");
1147                 len = dump_entry(&term[0], limited, use_predicate);
1148                 for (i = 1; i < termcount; i++)
1149                     len += dump_uses(tname[i], !(outform==F_TERMCAP || outform==F_TCONVERR));
1150                 putchar('\n');
1151                 if (itrace)
1152                     (void)fprintf(stderr,"infocmp: length %d\n", len);
1153                 break;
1154             }
1155         }
1156         else if (compare == C_USEALL)
1157             (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
1158         else if (compare == C_DEFAULT)
1159             (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
1160         else if (argc - optind != 2)
1161             (void) fprintf(stderr,
1162                 "File comparison needs exactly two file arguments.\n");
1163         else
1164             file_comparison(argc-optind, argv+optind);
1165
1166         ExitProgram(EXIT_SUCCESS);
1167 }
1168
1169 /* infocmp.c ends here */