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