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