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