1 /**************************************************************************** 2 * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 ****************************************************************************/ 33 34 /* 35 * tic.c --- Main program for terminfo compiler 36 * by Eric S. Raymond 37 * 38 */ 39 40 #include <progs.priv.h> 41 42 #include <dump_entry.h> 43 #include <term_entry.h> 44 45 MODULE_ID("$Id: tic.c,v 1.69 2000/04/08 23:53:49 tom Exp $") 46 47 const char *_nc_progname = "tic"; 48 49 static FILE *log_fp; 50 static FILE *tmp_fp; 51 static bool showsummary = FALSE; 52 static const char *to_remove; 53 54 static void (*save_check_termtype) (TERMTYPE *); 55 static void check_termtype(TERMTYPE * tt); 56 57 static const char usage_string[] = "[-h] [-v[n]] [-e names] [-CILNRTcfrswx1] source-file\n"; 58 59 static void 60 cleanup(void) 61 { 62 if (tmp_fp != 0) 63 fclose(tmp_fp); 64 if (to_remove != 0) { 65 #if HAVE_REMOVE 66 remove(to_remove); 67 #else 68 unlink(to_remove); 69 #endif 70 } 71 } 72 73 static void 74 failed(const char *msg) 75 { 76 perror(msg); 77 cleanup(); 78 exit(EXIT_FAILURE); 79 } 80 81 static void 82 usage(void) 83 { 84 static const char *const tbl[] = 85 { 86 "Options:", 87 " -1 format translation output one capability per line", 88 " -C translate entries to termcap source form", 89 " -I translate entries to terminfo source form", 90 " -L translate entries to full terminfo source form", 91 " -N disable smart defaults for source translation", 92 " -R restrict translation to given terminfo/termcap version", 93 " -T remove size-restrictions on compiled description", 94 #if NCURSES_XNAMES 95 " -a retain commented-out capabilities (sets -x also)", 96 #endif 97 " -c check only, validate input without compiling or translating", 98 " -f format complex strings for readability", 99 " -G format %{number} to %'char'", 100 " -g format %'char' to %{number}", 101 " -e<names> translate/compile only entries named by comma-separated list", 102 " -o<dir> set output directory for compiled entry writes", 103 " -r force resolution of all use entries in source translation", 104 " -s print summary statistics", 105 " -v[n] set verbosity level", 106 " -w[n] set format width for translation output", 107 #if NCURSES_XNAMES 108 " -x treat unknown capabilities as user-defined", 109 #endif 110 "", 111 "Parameters:", 112 " <file> file to translate or compile" 113 }; 114 size_t j; 115 116 fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string); 117 for (j = 0; j < sizeof(tbl) / sizeof(tbl[0]); j++) { 118 fputs(tbl[j], stderr); 119 putc('\n', stderr); 120 } 121 exit(EXIT_FAILURE); 122 } 123 124 #define L_BRACE '{' 125 #define R_BRACE '}' 126 #define S_QUOTE '\''; 127 128 static void 129 write_it(ENTRY * ep) 130 { 131 unsigned n; 132 int ch; 133 char *s, *d, *t; 134 char result[MAX_ENTRY_SIZE]; 135 136 /* 137 * Look for strings that contain %{number}, convert them to %'char', 138 * which is shorter and runs a little faster. 139 */ 140 for (n = 0; n < STRCOUNT; n++) { 141 s = ep->tterm.Strings[n]; 142 if (VALID_STRING(s) 143 && strchr(s, L_BRACE) != 0) { 144 d = result; 145 t = s; 146 while ((ch = *t++) != 0) { 147 *d++ = ch; 148 if (ch == '\\') { 149 *d++ = *t++; 150 } else if ((ch == '%') 151 && (*t == L_BRACE)) { 152 char *v = 0; 153 long value = strtol(t + 1, &v, 0); 154 if (v != 0 155 && *v == R_BRACE 156 && value > 0 157 && value != '\\' /* FIXME */ 158 && value < 127 159 && isprint((int) value)) { 160 *d++ = S_QUOTE; 161 *d++ = (int) value; 162 *d++ = S_QUOTE; 163 t = (v + 1); 164 } 165 } 166 } 167 *d = 0; 168 if (strlen(result) < strlen(s)) 169 strcpy(s, result); 170 } 171 } 172 173 _nc_set_type(_nc_first_name(ep->tterm.term_names)); 174 _nc_curr_line = ep->startline; 175 _nc_write_entry(&ep->tterm); 176 } 177 178 static bool 179 immedhook(ENTRY * ep GCC_UNUSED) 180 /* write out entries with no use capabilities immediately to save storage */ 181 { 182 #ifndef HAVE_BIG_CORE 183 /* 184 * This is strictly a core-economy kluge. The really clean way to handle 185 * compilation is to slurp the whole file into core and then do all the 186 * name-collision checks and entry writes in one swell foop. But the 187 * terminfo master file is large enough that some core-poor systems swap 188 * like crazy when you compile it this way...there have been reports of 189 * this process taking *three hours*, rather than the twenty seconds or 190 * less typical on my development box. 191 * 192 * So. This hook *immediately* writes out the referenced entry if it 193 * has no use capabilities. The compiler main loop refrains from 194 * adding the entry to the in-core list when this hook fires. If some 195 * other entry later needs to reference an entry that got written 196 * immediately, that's OK; the resolution code will fetch it off disk 197 * when it can't find it in core. 198 * 199 * Name collisions will still be detected, just not as cleanly. The 200 * write_entry() code complains before overwriting an entry that 201 * postdates the time of tic's first call to write_entry(). Thus 202 * it will complain about overwriting entries newly made during the 203 * tic run, but not about overwriting ones that predate it. 204 * 205 * The reason this is a hook, and not in line with the rest of the 206 * compiler code, is that the support for termcap fallback cannot assume 207 * it has anywhere to spool out these entries! 208 * 209 * The _nc_set_type() call here requires a compensating one in 210 * _nc_parse_entry(). 211 * 212 * If you define HAVE_BIG_CORE, you'll disable this kluge. This will 213 * make tic a bit faster (because the resolution code won't have to do 214 * disk I/O nearly as often). 215 */ 216 if (ep->nuses == 0) { 217 int oldline = _nc_curr_line; 218 219 write_it(ep); 220 _nc_curr_line = oldline; 221 free(ep->tterm.str_table); 222 return (TRUE); 223 } 224 #endif /* HAVE_BIG_CORE */ 225 return (FALSE); 226 } 227 228 static void 229 put_translate(int c) 230 /* emit a comment char, translating terminfo names to termcap names */ 231 { 232 static bool in_name = FALSE; 233 static size_t have, used; 234 static char *namebuf, *suffix; 235 236 if (in_name) { 237 if (used + 1 >= have) { 238 have += 132; 239 namebuf = typeRealloc(char, have, namebuf); 240 suffix = typeRealloc(char, have, suffix); 241 } 242 if (c == '\n' || c == '@') { 243 namebuf[used++] = '\0'; 244 (void) putchar('<'); 245 (void) fputs(namebuf, stdout); 246 putchar(c); 247 in_name = FALSE; 248 } else if (c != '>') { 249 namebuf[used++] = c; 250 } else { /* ah! candidate name! */ 251 char *up; 252 NCURSES_CONST char *tp; 253 254 namebuf[used++] = '\0'; 255 in_name = FALSE; 256 257 suffix[0] = '\0'; 258 if ((up = strchr(namebuf, '#')) != 0 259 || (up = strchr(namebuf, '=')) != 0 260 || ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) { 261 (void) strcpy(suffix, up); 262 *up = '\0'; 263 } 264 265 if ((tp = nametrans(namebuf)) != 0) { 266 (void) putchar(':'); 267 (void) fputs(tp, stdout); 268 (void) fputs(suffix, stdout); 269 (void) putchar(':'); 270 } else { 271 /* couldn't find a translation, just dump the name */ 272 (void) putchar('<'); 273 (void) fputs(namebuf, stdout); 274 (void) fputs(suffix, stdout); 275 (void) putchar('>'); 276 } 277 } 278 } else { 279 used = 0; 280 if (c == '<') { 281 in_name = TRUE; 282 } else { 283 putchar(c); 284 } 285 } 286 } 287 288 /* Returns a string, stripped of leading/trailing whitespace */ 289 static char * 290 stripped(char *src) 291 { 292 while (isspace(*src)) 293 src++; 294 if (*src != '\0') { 295 char *dst = strcpy(malloc(strlen(src) + 1), src); 296 size_t len = strlen(dst); 297 while (--len != 0 && isspace(dst[len])) 298 dst[len] = '\0'; 299 return dst; 300 } 301 return 0; 302 } 303 304 /* Parse the "-e" option-value into a list of names */ 305 static const char ** 306 make_namelist(char *src) 307 { 308 const char **dst = 0; 309 310 char *s, *base; 311 unsigned pass, n, nn; 312 char buffer[BUFSIZ]; 313 314 if (src == 0) { 315 /* EMPTY */ ; 316 } else if (strchr(src, '/') != 0) { /* a filename */ 317 FILE *fp = fopen(src, "r"); 318 if (fp == 0) 319 failed(src); 320 321 for (pass = 1; pass <= 2; pass++) { 322 nn = 0; 323 while (fgets(buffer, sizeof(buffer), fp) != 0) { 324 if ((s = stripped(buffer)) != 0) { 325 if (dst != 0) 326 dst[nn] = s; 327 nn++; 328 } 329 } 330 if (pass == 1) { 331 dst = typeCalloc(const char *, nn + 1); 332 rewind(fp); 333 } 334 } 335 fclose(fp); 336 } else { /* literal list of names */ 337 for (pass = 1; pass <= 2; pass++) { 338 for (n = nn = 0, base = src;; n++) { 339 int mark = src[n]; 340 if (mark == ',' || mark == '\0') { 341 if (pass == 1) { 342 nn++; 343 } else { 344 src[n] = '\0'; 345 if ((s = stripped(base)) != 0) 346 dst[nn++] = s; 347 base = &src[n + 1]; 348 } 349 } 350 if (mark == '\0') 351 break; 352 } 353 if (pass == 1) 354 dst = typeCalloc(const char *, nn + 1); 355 } 356 } 357 if (showsummary) { 358 fprintf(log_fp, "Entries that will be compiled:\n"); 359 for (n = 0; dst[n] != 0; n++) 360 fprintf(log_fp, "%d:%s\n", n + 1, dst[n]); 361 } 362 return dst; 363 } 364 365 static bool 366 matches(const char **needle, const char *haystack) 367 /* does entry in needle list match |-separated field in haystack? */ 368 { 369 bool code = FALSE; 370 size_t n; 371 372 if (needle != 0) { 373 for (n = 0; needle[n] != 0; n++) { 374 if (_nc_name_match(haystack, needle[n], "|")) { 375 code = TRUE; 376 break; 377 } 378 } 379 } else 380 code = TRUE; 381 return (code); 382 } 383 384 static FILE * 385 open_tempfile(char *name) 386 { 387 FILE *result = 0; 388 #if HAVE_MKSTEMP 389 int fd = mkstemp(name); 390 if (fd >= 0) 391 result = fdopen(fd, "w"); 392 #else 393 if (tmpnam(name) != 0) 394 result = fopen(name, "w"); 395 #endif 396 return result; 397 } 398 399 int 400 main(int argc, char *argv[]) 401 { 402 char my_tmpname[PATH_MAX]; 403 int v_opt = -1, debug_level; 404 int smart_defaults = TRUE; 405 char *termcap; 406 ENTRY *qp; 407 408 int this_opt, last_opt = '?'; 409 410 int outform = F_TERMINFO; /* output format */ 411 int sortmode = S_TERMINFO; /* sort_mode */ 412 413 int width = 60; 414 bool formatted = FALSE; /* reformat complex strings? */ 415 int numbers = 0; /* format "%'char'" to/from "%{number}" */ 416 bool infodump = FALSE; /* running as captoinfo? */ 417 bool capdump = FALSE; /* running as infotocap? */ 418 bool forceresolve = FALSE; /* force resolution */ 419 bool limited = TRUE; 420 char *tversion = (char *) NULL; 421 const char *source_file = "terminfo"; 422 const char **namelst = 0; 423 char *outdir = (char *) NULL; 424 bool check_only = FALSE; 425 426 log_fp = stderr; 427 428 if ((_nc_progname = strrchr(argv[0], '/')) == NULL) 429 _nc_progname = argv[0]; 430 else 431 _nc_progname++; 432 433 if ((infodump = (strcmp(_nc_progname, "captoinfo") == 0)) != FALSE) { 434 outform = F_TERMINFO; 435 sortmode = S_TERMINFO; 436 } 437 if ((capdump = (strcmp(_nc_progname, "infotocap") == 0)) != FALSE) { 438 outform = F_TERMCAP; 439 sortmode = S_TERMCAP; 440 } 441 #if NCURSES_XNAMES 442 use_extended_names(FALSE); 443 #endif 444 445 /* 446 * Processing arguments is a little complicated, since someone made a 447 * design decision to allow the numeric values for -w, -v options to 448 * be optional. 449 */ 450 while ((this_opt = getopt(argc, argv, 451 "0123456789CILNR:TVace:fGgo:rsvwx")) != EOF) { 452 if (isdigit(this_opt)) { 453 switch (last_opt) { 454 case 'v': 455 v_opt = (v_opt * 10) + (this_opt - '0'); 456 break; 457 case 'w': 458 width = (width * 10) + (this_opt - '0'); 459 break; 460 default: 461 if (this_opt != '1') 462 usage(); 463 last_opt = this_opt; 464 width = 0; 465 } 466 continue; 467 } 468 switch (this_opt) { 469 case 'C': 470 capdump = TRUE; 471 outform = F_TERMCAP; 472 sortmode = S_TERMCAP; 473 break; 474 case 'I': 475 infodump = TRUE; 476 outform = F_TERMINFO; 477 sortmode = S_TERMINFO; 478 break; 479 case 'L': 480 infodump = TRUE; 481 outform = F_VARIABLE; 482 sortmode = S_VARIABLE; 483 break; 484 case 'N': 485 smart_defaults = FALSE; 486 break; 487 case 'R': 488 tversion = optarg; 489 break; 490 case 'T': 491 limited = FALSE; 492 break; 493 case 'V': 494 puts(NCURSES_VERSION); 495 return EXIT_SUCCESS; 496 case 'c': 497 check_only = TRUE; 498 break; 499 case 'e': 500 namelst = make_namelist(optarg); 501 break; 502 case 'f': 503 formatted = TRUE; 504 break; 505 case 'G': 506 numbers = 1; 507 break; 508 case 'g': 509 numbers = -1; 510 break; 511 case 'o': 512 outdir = optarg; 513 break; 514 case 'r': 515 forceresolve = TRUE; 516 break; 517 case 's': 518 showsummary = TRUE; 519 break; 520 case 'v': 521 v_opt = 0; 522 break; 523 case 'w': 524 width = 0; 525 break; 526 #if NCURSES_XNAMES 527 case 'a': 528 _nc_disable_period = TRUE; 529 /* FALLTHRU */ 530 case 'x': 531 use_extended_names(TRUE); 532 break; 533 #endif 534 default: 535 usage(); 536 } 537 last_opt = this_opt; 538 } 539 540 debug_level = (v_opt > 0) ? v_opt : (v_opt == 0); 541 set_trace_level(debug_level); 542 543 if (_nc_tracing) { 544 save_check_termtype = _nc_check_termtype; 545 _nc_check_termtype = check_termtype; 546 } 547 #ifndef HAVE_BIG_CORE 548 /* 549 * Aaargh! immedhook seriously hoses us! 550 * 551 * One problem with immedhook is it means we can't do -e. Problem 552 * is that we can't guarantee that for each terminal listed, all the 553 * terminals it depends on will have been kept in core for reference 554 * resolution -- in fact it's certain the primitive types at the end 555 * of reference chains *won't* be in core unless they were explicitly 556 * in the select list themselves. 557 */ 558 if (namelst && (!infodump && !capdump)) { 559 (void) fprintf(stderr, 560 "Sorry, -e can't be used without -I or -C\n"); 561 cleanup(); 562 return EXIT_FAILURE; 563 } 564 #endif /* HAVE_BIG_CORE */ 565 566 if (optind < argc) { 567 source_file = argv[optind++]; 568 if (optind < argc) { 569 fprintf(stderr, 570 "%s: Too many file names. Usage:\n\t%s %s", 571 _nc_progname, 572 _nc_progname, 573 usage_string); 574 return EXIT_FAILURE; 575 } 576 } else { 577 if (infodump == TRUE) { 578 /* captoinfo's no-argument case */ 579 source_file = "/etc/termcap"; 580 if ((termcap = getenv("TERMCAP")) != 0 581 && (namelst = make_namelist(getenv("TERM"))) != 0) { 582 if (access(termcap, F_OK) == 0) { 583 /* file exists */ 584 source_file = termcap; 585 } else if ((tmp_fp = open_tempfile(my_tmpname)) != 0) { 586 source_file = my_tmpname; 587 fprintf(tmp_fp, "%s\n", termcap); 588 fclose(tmp_fp); 589 tmp_fp = fopen(source_file, "r"); 590 to_remove = source_file; 591 } else { 592 failed("tmpnam"); 593 } 594 } 595 } else { 596 /* tic */ 597 fprintf(stderr, 598 "%s: File name needed. Usage:\n\t%s %s", 599 _nc_progname, 600 _nc_progname, 601 usage_string); 602 cleanup(); 603 return EXIT_FAILURE; 604 } 605 } 606 607 if (tmp_fp == 0 608 && (tmp_fp = fopen(source_file, "r")) == 0) { 609 fprintf(stderr, "%s: Can't open %s\n", _nc_progname, source_file); 610 return EXIT_FAILURE; 611 } 612 613 if (infodump) 614 dump_init(tversion, 615 smart_defaults 616 ? outform 617 : F_LITERAL, 618 sortmode, width, debug_level, formatted); 619 else if (capdump) 620 dump_init(tversion, 621 outform, 622 sortmode, width, debug_level, FALSE); 623 624 /* parse entries out of the source file */ 625 _nc_set_source(source_file); 626 #ifndef HAVE_BIG_CORE 627 if (!(check_only || infodump || capdump)) 628 _nc_set_writedir(outdir); 629 #endif /* HAVE_BIG_CORE */ 630 _nc_read_entry_source(tmp_fp, (char *) NULL, 631 !smart_defaults, FALSE, 632 (check_only || infodump || capdump) ? NULLHOOK : immedhook); 633 634 /* do use resolution */ 635 if (check_only || (!infodump && !capdump) || forceresolve) { 636 if (!_nc_resolve_uses(TRUE) && !check_only) { 637 cleanup(); 638 return EXIT_FAILURE; 639 } 640 } 641 642 /* length check */ 643 if (check_only && (capdump || infodump)) { 644 for_entry_list(qp) { 645 if (matches(namelst, qp->tterm.term_names)) { 646 int len = fmt_entry(&qp->tterm, NULL, TRUE, infodump, numbers); 647 648 if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH)) 649 (void) fprintf(stderr, 650 "warning: resolved %s entry is %d bytes long\n", 651 _nc_first_name(qp->tterm.term_names), 652 len); 653 } 654 } 655 } 656 657 /* write or dump all entries */ 658 if (!check_only) { 659 if (!infodump && !capdump) { 660 _nc_set_writedir(outdir); 661 for_entry_list(qp) { 662 if (matches(namelst, qp->tterm.term_names)) 663 write_it(qp); 664 } 665 } else { 666 /* this is in case infotocap() generates warnings */ 667 _nc_curr_col = _nc_curr_line = -1; 668 669 for_entry_list(qp) { 670 if (matches(namelst, qp->tterm.term_names)) { 671 int j = qp->cend - qp->cstart; 672 int len = 0; 673 674 /* this is in case infotocap() generates warnings */ 675 _nc_set_type(_nc_first_name(qp->tterm.term_names)); 676 677 (void) fseek(tmp_fp, qp->cstart, SEEK_SET); 678 while (j--) { 679 if (infodump) 680 (void) putchar(fgetc(tmp_fp)); 681 else 682 put_translate(fgetc(tmp_fp)); 683 } 684 685 len = dump_entry(&qp->tterm, limited, numbers, NULL); 686 for (j = 0; j < qp->nuses; j++) 687 len += dump_uses(qp->uses[j].name, !capdump); 688 (void) putchar('\n'); 689 if (debug_level != 0 && !limited) 690 printf("# length=%d\n", len); 691 } 692 } 693 if (!namelst) { 694 int c, oldc = '\0'; 695 bool in_comment = FALSE; 696 bool trailing_comment = FALSE; 697 698 (void) fseek(tmp_fp, _nc_tail->cend, SEEK_SET); 699 while ((c = fgetc(tmp_fp)) != EOF) { 700 if (oldc == '\n') { 701 if (c == '#') { 702 trailing_comment = TRUE; 703 in_comment = TRUE; 704 } else { 705 in_comment = FALSE; 706 } 707 } 708 if (trailing_comment 709 && (in_comment || (oldc == '\n' && c == '\n'))) 710 putchar(c); 711 oldc = c; 712 } 713 } 714 } 715 } 716 717 /* Show the directory into which entries were written, and the total 718 * number of entries 719 */ 720 if (showsummary 721 && (!(check_only || infodump || capdump))) { 722 int total = _nc_tic_written(); 723 if (total != 0) 724 fprintf(log_fp, "%d entries written to %s\n", 725 total, 726 _nc_tic_dir((char *) 0)); 727 else 728 fprintf(log_fp, "No entries written\n"); 729 } 730 cleanup(); 731 return (EXIT_SUCCESS); 732 } 733 734 /* 735 * This bit of legerdemain turns all the terminfo variable names into 736 * references to locations in the arrays Booleans, Numbers, and Strings --- 737 * precisely what's needed (see comp_parse.c). 738 */ 739 740 TERMINAL *cur_term; /* tweak to avoid linking lib_cur_term.c */ 741 742 #undef CUR 743 #define CUR tp-> 744 745 /* 746 * An sgr string may contain several settings other than the one we're 747 * interested in, essentially sgr0 + rmacs + whatever. As long as the 748 * "whatever" is contained in the sgr string, that is close enough for our 749 * sanity check. 750 */ 751 static bool 752 similar_sgr(char *a, char *b) 753 { 754 while (*b != 0) { 755 while (*a != *b) { 756 if (*a == 0) 757 return FALSE; 758 a++; 759 } 760 a++; 761 b++; 762 } 763 return TRUE; 764 } 765 766 static void 767 check_sgr(TERMTYPE * tp, char *zero, int num, char *cap, const char *name) 768 { 769 char *test = tparm(set_attributes, 770 num == 1, 771 num == 2, 772 num == 3, 773 num == 4, 774 num == 5, 775 num == 6, 776 num == 7, 777 num == 8, 778 num == 9); 779 if (test != 0) { 780 if (PRESENT(cap)) { 781 if (!similar_sgr(test, cap)) { 782 _nc_warning("%s differs from sgr(%d): %s", name, num, 783 _nc_visbuf(test)); 784 } 785 } else if (strcmp(test, zero)) { 786 _nc_warning("sgr(%d) present, but not %s", num, name); 787 } 788 } else if (PRESENT(cap)) { 789 _nc_warning("sgr(%d) missing, but %s present", num, name); 790 } 791 } 792 793 #define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name) 794 795 /* other sanity-checks (things that we don't want in the normal 796 * logic that reads a terminfo entry) 797 */ 798 static void 799 check_termtype(TERMTYPE * tp) 800 { 801 bool conflict = FALSE; 802 unsigned j, k; 803 char fkeys[STRCOUNT]; 804 805 /* 806 * A terminal entry may contain more than one keycode assigned to 807 * a given string (e.g., KEY_END and KEY_LL). But curses will only 808 * return one (the last one assigned). 809 */ 810 memset(fkeys, 0, sizeof(fkeys)); 811 for (j = 0; _nc_tinfo_fkeys[j].code; j++) { 812 char *a = tp->Strings[_nc_tinfo_fkeys[j].offset]; 813 bool first = TRUE; 814 if (!VALID_STRING(a)) 815 continue; 816 for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) { 817 char *b = tp->Strings[_nc_tinfo_fkeys[k].offset]; 818 if (!VALID_STRING(b) 819 || fkeys[k]) 820 continue; 821 if (!strcmp(a, b)) { 822 fkeys[j] = 1; 823 fkeys[k] = 1; 824 if (first) { 825 if (!conflict) { 826 _nc_warning("Conflicting key definitions (using the last)"); 827 conflict = TRUE; 828 } 829 fprintf(stderr, "... %s is the same as %s", 830 keyname(_nc_tinfo_fkeys[j].code), 831 keyname(_nc_tinfo_fkeys[k].code)); 832 first = FALSE; 833 } else { 834 fprintf(stderr, ", %s", 835 keyname(_nc_tinfo_fkeys[k].code)); 836 } 837 } 838 } 839 if (!first) 840 fprintf(stderr, "\n"); 841 } 842 843 /* 844 * Quick check for color. We could also check if the ANSI versus 845 * non-ANSI strings are misused. 846 */ 847 if ((max_colors > 0) != (max_pairs > 0) 848 || (max_colors > max_pairs)) 849 _nc_warning("inconsistent values for max_colors and max_pairs"); 850 851 PAIRED(set_foreground, set_background); 852 PAIRED(set_a_foreground, set_a_background); 853 854 /* 855 * These may be mismatched because the terminal description relies on 856 * restoring the cursor visibility by resetting it. 857 */ 858 ANDMISSING(cursor_invisible, cursor_normal); 859 ANDMISSING(cursor_visible, cursor_normal); 860 861 if (PRESENT(cursor_visible) && PRESENT(cursor_normal) 862 && !strcmp(cursor_visible, cursor_normal)) 863 _nc_warning("cursor_visible is same as cursor_normal"); 864 865 /* 866 * From XSI & O'Reilly, we gather that sc/rc are required if csr is 867 * given, because the cursor position after the scrolling operation is 868 * performed is undefined. 869 */ 870 ANDMISSING(change_scroll_region, save_cursor); 871 ANDMISSING(change_scroll_region, restore_cursor); 872 873 if (PRESENT(set_attributes)) { 874 char *zero = tparm(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0); 875 876 zero = strdup(zero); 877 CHECK_SGR(1, enter_standout_mode); 878 CHECK_SGR(2, enter_underline_mode); 879 CHECK_SGR(3, enter_reverse_mode); 880 CHECK_SGR(4, enter_blink_mode); 881 CHECK_SGR(5, enter_dim_mode); 882 CHECK_SGR(6, enter_bold_mode); 883 CHECK_SGR(7, enter_secure_mode); 884 CHECK_SGR(8, enter_protected_mode); 885 CHECK_SGR(9, enter_alt_charset_mode); 886 free(zero); 887 } 888 889 /* 890 * Some standard applications (e.g., vi) and some non-curses 891 * applications (e.g., jove) get confused if we have both ich/ich1 and 892 * smir/rmir. Let's be nice and warn about that, too, even though 893 * ncurses handles it. 894 */ 895 if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode)) 896 && (PRESENT(insert_character) || PRESENT(parm_ich))) { 897 _nc_warning("non-curses applications may be confused by ich/ich1 with smir/rmir"); 898 } 899 900 /* 901 * Finally, do the non-verbose checks 902 */ 903 if (save_check_termtype != 0) 904 save_check_termtype(tp); 905 } 906