1 /**************************************************************************** 2 * Copyright (c) 1998-2001,2002 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 * and: Thomas E. Dickey 1996 on * 33 ****************************************************************************/ 34 35 #define __INTERNAL_CAPS_VISIBLE 36 #include <progs.priv.h> 37 38 #include "dump_entry.h" 39 #include "termsort.c" /* this C file is generated */ 40 #include <parametrized.h> /* so is this */ 41 42 MODULE_ID("$Id: dump_entry.c,v 1.58 2002/06/01 22:58:11 tom Exp $") 43 44 #define INDENT 8 45 #define DISCARD(string) string = ABSENT_STRING 46 #define PRINTF (void) printf 47 48 typedef struct { 49 char *text; 50 size_t used; 51 size_t size; 52 } DYNBUF; 53 54 static int tversion; /* terminfo version */ 55 static int outform; /* output format to use */ 56 static int sortmode; /* sort mode to use */ 57 static int width = 60; /* max line width for listings */ 58 static int column; /* current column, limited by 'width' */ 59 static int oldcol; /* last value of column before wrap */ 60 static bool pretty; /* true if we format if-then-else strings */ 61 62 static DYNBUF outbuf; 63 static DYNBUF tmpbuf; 64 65 /* indirection pointers for implementing sort and display modes */ 66 static const int *bool_indirect, *num_indirect, *str_indirect; 67 static NCURSES_CONST char *const *bool_names; 68 static NCURSES_CONST char *const *num_names; 69 static NCURSES_CONST char *const *str_names; 70 71 static const char *separator, *trailer; 72 73 /* cover various ports and variants of terminfo */ 74 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */ 75 #define V_SVR1 1 /* SVR1, Ultrix */ 76 #define V_HPUX 2 /* HP/UX */ 77 #define V_AIX 3 /* AIX */ 78 #define V_BSD 4 /* BSD */ 79 80 #if NCURSES_XNAMES 81 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T')) 82 #else 83 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T') 84 #endif 85 86 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n)) 87 88 #if NCURSES_XNAMES 89 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j])) 90 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j])) 91 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j])) 92 #else 93 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j]) 94 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j]) 95 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j]) 96 #endif 97 98 static void 99 strncpy_DYN(DYNBUF * dst, const char *src, size_t need) 100 { 101 size_t want = need + dst->used + 1; 102 if (want > dst->size) { 103 dst->size += (want + 1024); /* be generous */ 104 dst->text = typeRealloc(char, dst->size, dst->text); 105 } 106 (void) strncpy(dst->text + dst->used, src, need); 107 dst->used += need; 108 dst->text[dst->used] = 0; 109 } 110 111 static void 112 strcpy_DYN(DYNBUF * dst, const char *src) 113 { 114 if (src == 0) { 115 dst->used = 0; 116 strcpy_DYN(dst, ""); 117 } else { 118 strncpy_DYN(dst, src, strlen(src)); 119 } 120 } 121 122 #if NO_LEAKS 123 static void 124 free_DYN(DYNBUF * p) 125 { 126 if (p->text != 0) 127 free(p->text); 128 p->text = 0; 129 p->size = 0; 130 p->used = 0; 131 } 132 133 void 134 _nc_leaks_dump_entry(void) 135 { 136 free_DYN(&outbuf); 137 free_DYN(&tmpbuf); 138 } 139 #endif 140 141 NCURSES_CONST char * 142 nametrans(const char *name) 143 /* translate a capability name from termcap to terminfo */ 144 { 145 const struct name_table_entry *np; 146 147 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) 148 switch (np->nte_type) { 149 case BOOLEAN: 150 if (bool_from_termcap[np->nte_index]) 151 return (boolcodes[np->nte_index]); 152 break; 153 154 case NUMBER: 155 if (num_from_termcap[np->nte_index]) 156 return (numcodes[np->nte_index]); 157 break; 158 159 case STRING: 160 if (str_from_termcap[np->nte_index]) 161 return (strcodes[np->nte_index]); 162 break; 163 } 164 165 return (0); 166 } 167 168 void 169 dump_init(const char *version, int mode, int sort, int twidth, int traceval, 170 bool formatted) 171 /* set up for entry display */ 172 { 173 width = twidth; 174 pretty = formatted; 175 176 /* versions */ 177 if (version == 0) 178 tversion = V_ALLCAPS; 179 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1") 180 || !strcmp(version, "Ultrix")) 181 tversion = V_SVR1; 182 else if (!strcmp(version, "HP")) 183 tversion = V_HPUX; 184 else if (!strcmp(version, "AIX")) 185 tversion = V_AIX; 186 else if (!strcmp(version, "BSD")) 187 tversion = V_BSD; 188 else 189 tversion = V_ALLCAPS; 190 191 /* implement display modes */ 192 switch (outform = mode) { 193 case F_LITERAL: 194 case F_TERMINFO: 195 bool_names = boolnames; 196 num_names = numnames; 197 str_names = strnames; 198 separator = twidth ? ", " : ","; 199 trailer = "\n\t"; 200 break; 201 202 case F_VARIABLE: 203 bool_names = boolfnames; 204 num_names = numfnames; 205 str_names = strfnames; 206 separator = twidth ? ", " : ","; 207 trailer = "\n\t"; 208 break; 209 210 case F_TERMCAP: 211 case F_TCONVERR: 212 bool_names = boolcodes; 213 num_names = numcodes; 214 str_names = strcodes; 215 separator = ":"; 216 trailer = "\\\n\t:"; 217 break; 218 } 219 220 /* implement sort modes */ 221 switch (sortmode = sort) { 222 case S_NOSORT: 223 if (traceval) 224 (void) fprintf(stderr, 225 "%s: sorting by term structure order\n", _nc_progname); 226 break; 227 228 case S_TERMINFO: 229 if (traceval) 230 (void) fprintf(stderr, 231 "%s: sorting by terminfo name order\n", _nc_progname); 232 bool_indirect = bool_terminfo_sort; 233 num_indirect = num_terminfo_sort; 234 str_indirect = str_terminfo_sort; 235 break; 236 237 case S_VARIABLE: 238 if (traceval) 239 (void) fprintf(stderr, 240 "%s: sorting by C variable order\n", _nc_progname); 241 bool_indirect = bool_variable_sort; 242 num_indirect = num_variable_sort; 243 str_indirect = str_variable_sort; 244 break; 245 246 case S_TERMCAP: 247 if (traceval) 248 (void) fprintf(stderr, 249 "%s: sorting by termcap name order\n", _nc_progname); 250 bool_indirect = bool_termcap_sort; 251 num_indirect = num_termcap_sort; 252 str_indirect = str_termcap_sort; 253 break; 254 } 255 256 if (traceval) 257 (void) fprintf(stderr, 258 "%s: width = %d, tversion = %d, outform = %d\n", 259 _nc_progname, width, tversion, outform); 260 } 261 262 static TERMTYPE *cur_type; 263 264 static int 265 dump_predicate(int type, int idx) 266 /* predicate function to use for ordinary decompilation */ 267 { 268 switch (type) { 269 case BOOLEAN: 270 return (cur_type->Booleans[idx] == FALSE) 271 ? FAIL : cur_type->Booleans[idx]; 272 273 case NUMBER: 274 return (cur_type->Numbers[idx] == ABSENT_NUMERIC) 275 ? FAIL : cur_type->Numbers[idx]; 276 277 case STRING: 278 return (cur_type->Strings[idx] != ABSENT_STRING) 279 ? (int) TRUE : FAIL; 280 } 281 282 return (FALSE); /* pacify compiler */ 283 } 284 285 static void set_obsolete_termcaps(TERMTYPE * tp); 286 287 /* is this the index of a function key string? */ 288 #define FNKEY(i) (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268)) 289 290 static bool 291 version_filter(int type, int idx) 292 /* filter out capabilities we may want to suppress */ 293 { 294 switch (tversion) { 295 case V_ALLCAPS: /* SVr4, XSI Curses */ 296 return (TRUE); 297 298 case V_SVR1: /* System V Release 1, Ultrix */ 299 switch (type) { 300 case BOOLEAN: 301 /* below and including xon_xoff */ 302 return ((idx <= 20) ? TRUE : FALSE); 303 case NUMBER: 304 /* below and including width_status_line */ 305 return ((idx <= 7) ? TRUE : FALSE); 306 case STRING: 307 /* below and including prtr_non */ 308 return ((idx <= 144) ? TRUE : FALSE); 309 } 310 break; 311 312 case V_HPUX: /* Hewlett-Packard */ 313 switch (type) { 314 case BOOLEAN: 315 /* below and including xon_xoff */ 316 return ((idx <= 20) ? TRUE : FALSE); 317 case NUMBER: 318 /* below and including label_width */ 319 return ((idx <= 10) ? TRUE : FALSE); 320 case STRING: 321 if (idx <= 144) /* below and including prtr_non */ 322 return (TRUE); 323 else if (FNKEY(idx)) /* function keys */ 324 return (TRUE); 325 else if (idx == 147 || idx == 156 || idx == 157) /* plab_norm,label_on,label_off */ 326 return (TRUE); 327 else 328 return (FALSE); 329 } 330 break; 331 332 case V_AIX: /* AIX */ 333 switch (type) { 334 case BOOLEAN: 335 /* below and including xon_xoff */ 336 return ((idx <= 20) ? TRUE : FALSE); 337 case NUMBER: 338 /* below and including width_status_line */ 339 return ((idx <= 7) ? TRUE : FALSE); 340 case STRING: 341 if (idx <= 144) /* below and including prtr_non */ 342 return (TRUE); 343 else if (FNKEY(idx)) /* function keys */ 344 return (TRUE); 345 else 346 return (FALSE); 347 } 348 break; 349 350 case V_BSD: /* BSD */ 351 switch (type) { 352 case BOOLEAN: 353 return bool_from_termcap[idx]; 354 case NUMBER: 355 return num_from_termcap[idx]; 356 case STRING: 357 return str_from_termcap[idx]; 358 } 359 break; 360 } 361 362 return (FALSE); /* pacify the compiler */ 363 } 364 365 static void 366 force_wrap(void) 367 { 368 oldcol = column; 369 strcpy_DYN(&outbuf, trailer); 370 column = INDENT; 371 } 372 373 static void 374 wrap_concat(const char *src) 375 { 376 int need = strlen(src); 377 int want = strlen(separator) + need; 378 379 if (column > INDENT 380 && column + want > width) { 381 force_wrap(); 382 } 383 strcpy_DYN(&outbuf, src); 384 strcpy_DYN(&outbuf, separator); 385 column += need; 386 } 387 388 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \ 389 if ((size_t)(last - first) > sizeof(sep_trail)-1 \ 390 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \ 391 first += sizeof(sep_trail)-2 392 393 /* Returns the nominal length of the buffer assuming it is termcap format, 394 * i.e., the continuation sequence is treated as a single character ":". 395 * 396 * There are several implementations of termcap which read the text into a 397 * fixed-size buffer. Generally they strip the newlines from the text, but may 398 * not do it until after the buffer is read. Also, "tc=" resolution may be 399 * expanded in the same buffer. This function is useful for measuring the size 400 * of the best fixed-buffer implementation; the worst case may be much worse. 401 */ 402 #ifdef TEST_TERMCAP_LENGTH 403 static int 404 termcap_length(const char *src) 405 { 406 static const char pattern[] = ":\\\n\t:"; 407 408 int len = 0; 409 const char *const t = src + strlen(src); 410 411 while (*src != '\0') { 412 IGNORE_SEP_TRAIL(src, t, pattern); 413 src++; 414 len++; 415 } 416 return len; 417 } 418 #else 419 #define termcap_length(src) strlen(src) 420 #endif 421 422 static char * 423 fmt_complex(char *src, int level) 424 { 425 int percent = 0; 426 int n; 427 bool if_then = strstr(src, "%?") != 0; 428 bool params = !if_then && (strlen(src) > 50) && (strstr(src, "%p") != 0); 429 430 while (*src != '\0') { 431 switch (*src) { 432 case '\\': 433 percent = 0; 434 strncpy_DYN(&tmpbuf, src++, 1); 435 break; 436 case '%': 437 percent = 1; 438 break; 439 case '?': /* "if" */ 440 case 't': /* "then" */ 441 case 'e': /* "else" */ 442 if (percent) { 443 percent = 0; 444 tmpbuf.text[tmpbuf.used - 1] = '\n'; 445 /* treat a "%e%?" as else-if, on the same level */ 446 if (!strncmp(src, "e%?", 3)) { 447 for (n = 0; n < level; n++) 448 strncpy_DYN(&tmpbuf, "\t", 1); 449 strncpy_DYN(&tmpbuf, "%", 1); 450 strncpy_DYN(&tmpbuf, src, 3); 451 src += 3; 452 } else { 453 for (n = 0; n <= level; n++) 454 strncpy_DYN(&tmpbuf, "\t", 1); 455 strncpy_DYN(&tmpbuf, "%", 1); 456 strncpy_DYN(&tmpbuf, src, 1); 457 if (*src++ == '?') { 458 src = fmt_complex(src, level + 1); 459 } else if (level == 1) { 460 _nc_warning("%%%c without %%?", *src); 461 } 462 } 463 continue; 464 } 465 break; 466 case ';': /* "endif" */ 467 if (percent) { 468 percent = 0; 469 if (level > 1) { 470 tmpbuf.text[tmpbuf.used - 1] = '\n'; 471 for (n = 0; n < level; n++) 472 strncpy_DYN(&tmpbuf, "\t", 1); 473 strncpy_DYN(&tmpbuf, "%", 1); 474 strncpy_DYN(&tmpbuf, src++, 1); 475 return src; 476 } 477 _nc_warning("%%; without %%?"); 478 } 479 break; 480 case 'p': 481 if (percent && params) { 482 tmpbuf.text[tmpbuf.used - 1] = '\n'; 483 for (n = 0; n <= level; n++) 484 strncpy_DYN(&tmpbuf, "\t", 1); 485 strncpy_DYN(&tmpbuf, "%", 1); 486 } 487 percent = 0; 488 break; 489 default: 490 percent = 0; 491 break; 492 } 493 strncpy_DYN(&tmpbuf, src++, 1); 494 } 495 return src; 496 } 497 498 int 499 fmt_entry(TERMTYPE * tterm, 500 int (*pred) (int type, int idx), 501 bool suppress_untranslatable, 502 bool infodump, 503 int numbers) 504 { 505 int i, j; 506 char buffer[MAX_TERMINFO_LENGTH]; 507 NCURSES_CONST char *name; 508 int predval, len; 509 int num_bools = 0; 510 int num_values = 0; 511 int num_strings = 0; 512 bool outcount = 0; 513 514 #define WRAP_CONCAT \ 515 wrap_concat(buffer); \ 516 outcount = TRUE 517 518 len = 12; /* terminfo file-header */ 519 520 if (pred == 0) { 521 cur_type = tterm; 522 pred = dump_predicate; 523 } 524 525 strcpy_DYN(&outbuf, 0); 526 strcpy_DYN(&outbuf, tterm->term_names); 527 strcpy_DYN(&outbuf, separator); 528 column = outbuf.used; 529 force_wrap(); 530 531 for_each_boolean(j, tterm) { 532 i = BoolIndirect(j); 533 name = ExtBoolname(tterm, i, bool_names); 534 535 if (!version_filter(BOOLEAN, i)) 536 continue; 537 else if (isObsolete(outform, name)) 538 continue; 539 540 predval = pred(BOOLEAN, i); 541 if (predval != FAIL) { 542 (void) strcpy(buffer, name); 543 if (predval <= 0) 544 (void) strcat(buffer, "@"); 545 else if (i + 1 > num_bools) 546 num_bools = i + 1; 547 WRAP_CONCAT; 548 } 549 } 550 551 if (column != INDENT) 552 force_wrap(); 553 554 for_each_number(j, tterm) { 555 i = NumIndirect(j); 556 name = ExtNumname(tterm, i, num_names); 557 558 if (!version_filter(NUMBER, i)) 559 continue; 560 else if (isObsolete(outform, name)) 561 continue; 562 563 predval = pred(NUMBER, i); 564 if (predval != FAIL) { 565 if (tterm->Numbers[i] < 0) { 566 sprintf(buffer, "%s@", name); 567 } else { 568 sprintf(buffer, "%s#%d", name, tterm->Numbers[i]); 569 if (i + 1 > num_values) 570 num_values = i + 1; 571 } 572 WRAP_CONCAT; 573 } 574 } 575 576 if (column != INDENT) 577 force_wrap(); 578 579 len += num_bools 580 + num_values * 2 581 + strlen(tterm->term_names) + 1; 582 if (len & 1) 583 len++; 584 585 #undef CUR 586 #define CUR tterm-> 587 if (outform == F_TERMCAP) { 588 if (termcap_reset != ABSENT_STRING) { 589 if (init_3string != ABSENT_STRING 590 && !strcmp(init_3string, termcap_reset)) 591 DISCARD(init_3string); 592 593 if (reset_2string != ABSENT_STRING 594 && !strcmp(reset_2string, termcap_reset)) 595 DISCARD(reset_2string); 596 } 597 } 598 599 for_each_string(j, tterm) { 600 i = StrIndirect(j); 601 name = ExtStrname(tterm, i, str_names); 602 603 if (!version_filter(STRING, i)) 604 continue; 605 else if (isObsolete(outform, name)) 606 continue; 607 608 /* 609 * Some older versions of vi want rmir/smir to be defined 610 * for ich/ich1 to work. If they're not defined, force 611 * them to be output as defined and empty. 612 */ 613 if (outform == F_TERMCAP) { 614 if (insert_character || parm_ich) { 615 if (&tterm->Strings[i] == &enter_insert_mode 616 && enter_insert_mode == ABSENT_STRING) { 617 (void) strcpy(buffer, "im="); 618 WRAP_CONCAT; 619 continue; 620 } 621 622 if (&tterm->Strings[i] == &exit_insert_mode 623 && exit_insert_mode == ABSENT_STRING) { 624 (void) strcpy(buffer, "ei="); 625 WRAP_CONCAT; 626 continue; 627 } 628 } 629 } 630 631 predval = pred(STRING, i); 632 buffer[0] = '\0'; 633 634 if (predval != FAIL) { 635 if (tterm->Strings[i] != ABSENT_STRING 636 && i + 1 > num_strings) 637 num_strings = i + 1; 638 639 if (!VALID_STRING(tterm->Strings[i])) { 640 sprintf(buffer, "%s@", name); 641 WRAP_CONCAT; 642 } else if (outform == F_TERMCAP || outform == F_TCONVERR) { 643 int params = ((i < (int) SIZEOF(parametrized)) 644 ? parametrized[i] 645 : 0); 646 char *srccap = _nc_tic_expand(tterm->Strings[i], TRUE, numbers); 647 char *cv = _nc_infotocap(name, srccap, params); 648 649 if (cv == 0) { 650 if (outform == F_TCONVERR) { 651 sprintf(buffer, "%s=!!! %s WILL NOT CONVERT !!!", 652 name, srccap); 653 } else if (suppress_untranslatable) { 654 continue; 655 } else { 656 char *s = srccap, *d = buffer; 657 sprintf(d, "..%s=", name); 658 d += strlen(d); 659 while ((*d = *s++) != 0) { 660 if (*d == ':') { 661 *d++ = '\\'; 662 *d = ':'; 663 } else if (*d == '\\') { 664 *++d = *s++; 665 } 666 d++; 667 } 668 } 669 } else { 670 sprintf(buffer, "%s=%s", name, cv); 671 } 672 len += strlen(tterm->Strings[i]) + 1; 673 WRAP_CONCAT; 674 } else { 675 char *src = _nc_tic_expand(tterm->Strings[i], 676 outform == F_TERMINFO, numbers); 677 678 strcpy_DYN(&tmpbuf, 0); 679 strcpy_DYN(&tmpbuf, name); 680 strcpy_DYN(&tmpbuf, "="); 681 if (pretty 682 && (outform == F_TERMINFO 683 || outform == F_VARIABLE)) { 684 fmt_complex(src, 1); 685 } else { 686 strcpy_DYN(&tmpbuf, src); 687 } 688 len += strlen(tterm->Strings[i]) + 1; 689 wrap_concat(tmpbuf.text); 690 outcount = TRUE; 691 } 692 } 693 } 694 len += num_strings * 2; 695 696 /* 697 * This piece of code should be an effective inverse of the functions 698 * postprocess_terminfo and postprocess_terminfo in parse_entry.c. 699 * Much more work should be done on this to support dumping termcaps. 700 */ 701 if (tversion == V_HPUX) { 702 if (memory_lock) { 703 (void) sprintf(buffer, "meml=%s", memory_lock); 704 WRAP_CONCAT; 705 } 706 if (memory_unlock) { 707 (void) sprintf(buffer, "memu=%s", memory_unlock); 708 WRAP_CONCAT; 709 } 710 } else if (tversion == V_AIX) { 711 if (VALID_STRING(acs_chars)) { 712 bool box_ok = TRUE; 713 const char *acstrans = "lqkxjmwuvtn"; 714 const char *cp; 715 char *tp, *sp, boxchars[11]; 716 717 tp = boxchars; 718 for (cp = acstrans; *cp; cp++) { 719 sp = strchr(acs_chars, *cp); 720 if (sp) 721 *tp++ = sp[1]; 722 else { 723 box_ok = FALSE; 724 break; 725 } 726 } 727 tp[0] = '\0'; 728 729 if (box_ok) { 730 (void) strcpy(buffer, "box1="); 731 (void) strcat(buffer, _nc_tic_expand(boxchars, 732 outform == F_TERMINFO, numbers)); 733 WRAP_CONCAT; 734 } 735 } 736 } 737 738 /* 739 * kludge: trim off trailer to avoid an extra blank line 740 * in infocmp -u output when there are no string differences 741 */ 742 if (outcount) { 743 bool trimmed = FALSE; 744 j = outbuf.used; 745 if (j >= 2 746 && outbuf.text[j - 1] == '\t' 747 && outbuf.text[j - 2] == '\n') { 748 outbuf.used -= 2; 749 trimmed = TRUE; 750 } else if (j >= 4 751 && outbuf.text[j - 1] == ':' 752 && outbuf.text[j - 2] == '\t' 753 && outbuf.text[j - 3] == '\n' 754 && outbuf.text[j - 4] == '\\') { 755 outbuf.used -= 4; 756 trimmed = TRUE; 757 } 758 if (trimmed) { 759 outbuf.text[outbuf.used] = '\0'; 760 column = oldcol; 761 } 762 } 763 #if 0 764 fprintf(stderr, "num_bools = %d\n", num_bools); 765 fprintf(stderr, "num_values = %d\n", num_values); 766 fprintf(stderr, "num_strings = %d\n", num_strings); 767 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", 768 tterm->term_names, len, outbuf.used, outbuf.text); 769 #endif 770 /* 771 * Here's where we use infodump to trigger a more stringent length check 772 * for termcap-translation purposes. 773 * Return the length of the raw entry, without tc= expansions, 774 * It gives an idea of which entries are deadly to even *scan past*, 775 * as opposed to *use*. 776 */ 777 return (infodump ? len : (int) termcap_length(outbuf.text)); 778 } 779 780 static bool 781 kill_string(TERMTYPE * tterm, char *cap) 782 { 783 int n; 784 for (n = 0; n < tterm->num_Strings; ++n) { 785 if (cap == tterm->Strings[n]) { 786 tterm->Strings[n] = ABSENT_STRING; 787 return TRUE; 788 } 789 } 790 return FALSE; 791 } 792 793 static char * 794 find_string(TERMTYPE * tterm, char *name) 795 { 796 int n; 797 for (n = 0; n < tterm->num_Strings; ++n) { 798 if (version_filter(STRING, n) 799 && !strcmp(name, strnames[n])) { 800 char *cap = tterm->Strings[n]; 801 if (VALID_STRING(cap)) { 802 return cap; 803 } 804 break; 805 } 806 } 807 return ABSENT_STRING; 808 } 809 810 /* 811 * This is used to remove function-key labels from a termcap entry to 812 * make it smaller. 813 */ 814 static int 815 kill_labels(TERMTYPE * tterm, int target) 816 { 817 int n; 818 int result = 0; 819 char *cap; 820 char name[10]; 821 822 for (n = 0; n <= 10; ++n) { 823 sprintf(name, "lf%d", n); 824 if ((cap = find_string(tterm, name)) != ABSENT_STRING 825 && kill_string(tterm, cap)) { 826 target -= (strlen(cap) + 5); 827 ++result; 828 if (target < 0) 829 break; 830 } 831 } 832 return result; 833 } 834 835 /* 836 * This is used to remove function-key definitions from a termcap entry to 837 * make it smaller. 838 */ 839 static int 840 kill_fkeys(TERMTYPE * tterm, int target) 841 { 842 int n; 843 int result = 0; 844 char *cap; 845 char name[10]; 846 847 for (n = 60; n >= 0; --n) { 848 sprintf(name, "kf%d", n); 849 if ((cap = find_string(tterm, name)) != ABSENT_STRING 850 && kill_string(tterm, cap)) { 851 target -= (strlen(cap) + 5); 852 ++result; 853 if (target < 0) 854 break; 855 } 856 } 857 return result; 858 } 859 860 int 861 dump_entry(TERMTYPE * tterm, 862 bool limited, 863 int numbers, 864 int (*pred) (int type, int idx)) 865 /* dump a single entry */ 866 { 867 int len, critlen; 868 const char *legend; 869 bool infodump; 870 871 if (outform == F_TERMCAP || outform == F_TCONVERR) { 872 critlen = MAX_TERMCAP_LENGTH; 873 legend = "older termcap"; 874 infodump = FALSE; 875 set_obsolete_termcaps(tterm); 876 } else { 877 critlen = MAX_TERMINFO_LENGTH; 878 legend = "terminfo"; 879 infodump = TRUE; 880 } 881 882 if (((len = fmt_entry(tterm, pred, FALSE, infodump, numbers)) > critlen) 883 && limited) { 884 PRINTF("# (untranslatable capabilities removed to fit entry within %d bytes)\n", 885 critlen); 886 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 887 /* 888 * We pick on sgr because it's a nice long string capability that 889 * is really just an optimization hack. Another good candidate is 890 * acsc since it is both long and unused by BSD termcap. 891 */ 892 char *oldsgr = set_attributes; 893 char *oldacsc = acs_chars; 894 set_attributes = ABSENT_STRING; 895 PRINTF("# (sgr removed to fit entry within %d bytes)\n", 896 critlen); 897 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 898 acs_chars = ABSENT_STRING; 899 PRINTF("# (acsc removed to fit entry within %d bytes)\n", 900 critlen); 901 } 902 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 903 int oldversion = tversion; 904 905 tversion = V_BSD; 906 PRINTF("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", 907 critlen); 908 909 len = fmt_entry(tterm, pred, TRUE, infodump, numbers); 910 if (len > critlen 911 && kill_labels(tterm, len - critlen)) { 912 PRINTF("# (some labels capabilities suppressed to fit entry within %d bytes)\n", 913 critlen); 914 len = fmt_entry(tterm, pred, TRUE, infodump, numbers); 915 } 916 if (len > critlen 917 && kill_fkeys(tterm, len - critlen)) { 918 PRINTF("# (some function-key capabilities suppressed to fit entry within %d bytes)\n", 919 critlen); 920 len = fmt_entry(tterm, pred, TRUE, infodump, numbers); 921 } 922 if (len > critlen) { 923 (void) fprintf(stderr, 924 "warning: %s entry is %d bytes long\n", 925 _nc_first_name(tterm->term_names), 926 len); 927 PRINTF( 928 "# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", 929 len, legend); 930 } 931 tversion = oldversion; 932 } 933 set_attributes = oldsgr; 934 acs_chars = oldacsc; 935 } 936 } 937 938 (void) fputs(outbuf.text, stdout); 939 return len; 940 } 941 942 int 943 dump_uses(const char *name, bool infodump) 944 /* dump "use=" clauses in the appropriate format */ 945 { 946 char buffer[MAX_TERMINFO_LENGTH]; 947 948 strcpy_DYN(&outbuf, 0); 949 (void) sprintf(buffer, "%s%s", infodump ? "use=" : "tc=", name); 950 wrap_concat(buffer); 951 (void) fputs(outbuf.text, stdout); 952 return outbuf.used; 953 } 954 955 void 956 compare_entry(void (*hook) (int t, int i, const char *name), TERMTYPE * tp 957 GCC_UNUSED, bool quiet) 958 /* compare two entries */ 959 { 960 int i, j; 961 NCURSES_CONST char *name; 962 963 if (!quiet) 964 fputs(" comparing booleans.\n", stdout); 965 for_each_boolean(j, tp) { 966 i = BoolIndirect(j); 967 name = ExtBoolname(tp, i, bool_names); 968 969 if (isObsolete(outform, name)) 970 continue; 971 972 (*hook) (CMP_BOOLEAN, i, name); 973 } 974 975 if (!quiet) 976 fputs(" comparing numbers.\n", stdout); 977 for_each_number(j, tp) { 978 i = NumIndirect(j); 979 name = ExtNumname(tp, i, num_names); 980 981 if (isObsolete(outform, name)) 982 continue; 983 984 (*hook) (CMP_NUMBER, i, name); 985 } 986 987 if (!quiet) 988 fputs(" comparing strings.\n", stdout); 989 for_each_string(j, tp) { 990 i = StrIndirect(j); 991 name = ExtStrname(tp, i, str_names); 992 993 if (isObsolete(outform, name)) 994 continue; 995 996 (*hook) (CMP_STRING, i, name); 997 } 998 999 /* (void) fputs(" comparing use entries.\n", stdout); */ 1000 (*hook) (CMP_USE, 0, "use"); 1001 1002 } 1003 1004 #define NOTSET(s) ((s) == 0) 1005 1006 /* 1007 * This bit of legerdemain turns all the terminfo variable names into 1008 * references to locations in the arrays Booleans, Numbers, and Strings --- 1009 * precisely what's needed. 1010 */ 1011 #undef CUR 1012 #define CUR tp-> 1013 1014 static void 1015 set_obsolete_termcaps(TERMTYPE * tp) 1016 { 1017 #include "capdefaults.c" 1018 } 1019 1020 /* 1021 * Convert an alternate-character-set string to canonical form: sorted and 1022 * unique. 1023 */ 1024 void 1025 repair_acsc(TERMTYPE * tp) 1026 { 1027 if (VALID_STRING(acs_chars)) { 1028 size_t n, m; 1029 char mapped[256]; 1030 char extra = 0; 1031 unsigned source; 1032 unsigned target; 1033 bool fix_needed = FALSE; 1034 1035 for (n = 0, source = 0; acs_chars[n] != 0; n++) { 1036 target = acs_chars[n]; 1037 if (source >= target) { 1038 fix_needed = TRUE; 1039 break; 1040 } 1041 source = target; 1042 if (acs_chars[n + 1]) 1043 n++; 1044 } 1045 if (fix_needed) { 1046 memset(mapped, 0, sizeof(mapped)); 1047 for (n = 0; acs_chars[n] != 0; n++) { 1048 source = acs_chars[n]; 1049 if ((target = (unsigned char) acs_chars[n + 1]) != 0) { 1050 mapped[source] = target; 1051 n++; 1052 } else { 1053 extra = source; 1054 } 1055 } 1056 for (n = m = 0; n < sizeof(mapped); n++) { 1057 if (mapped[n]) { 1058 acs_chars[m++] = n; 1059 acs_chars[m++] = mapped[n]; 1060 } 1061 } 1062 if (extra) 1063 acs_chars[m++] = extra; /* garbage in, garbage out */ 1064 acs_chars[m] = 0; 1065 } 1066 } 1067 } 1068