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