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