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