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