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