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