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