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