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