1 /**************************************************************************** 2 * Copyright 2018-2019,2020 Thomas E. Dickey * 3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. * 4 * * 5 * Permission is hereby granted, free of charge, to any person obtaining a * 6 * copy of this software and associated documentation files (the * 7 * "Software"), to deal in the Software without restriction, including * 8 * without limitation the rights to use, copy, modify, merge, publish, * 9 * distribute, distribute with modifications, sublicense, and/or sell * 10 * copies of the Software, and to permit persons to whom the Software is * 11 * furnished to do so, subject to the following conditions: * 12 * * 13 * The above copyright notice and this permission notice shall be included * 14 * in all copies or substantial portions of the Software. * 15 * * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 23 * * 24 * Except as contained in this notice, the name(s) of the above copyright * 25 * holders shall not be used in advertising or otherwise to promote the * 26 * sale, use or other dealings in this Software without prior written * 27 * authorization. * 28 ****************************************************************************/ 29 30 /**************************************************************************** 31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 32 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 33 * and: Thomas E. Dickey 1996 on * 34 ****************************************************************************/ 35 36 #define __INTERNAL_CAPS_VISIBLE 37 #include <progs.priv.h> 38 39 #include "dump_entry.h" 40 #include "termsort.c" /* this C file is generated */ 41 #include <parametrized.h> /* so is this */ 42 43 MODULE_ID("$Id: dump_entry.c,v 1.176 2020/02/02 23:34:34 tom Exp $") 44 45 #define DISCARD(string) string = ABSENT_STRING 46 #define PRINTF (void) printf 47 #define WRAPPED 32 48 49 #define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array)) 50 #define TcOutput() (outform == F_TERMCAP || outform == F_TCONVERR) 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 height = 65535; /* max number of lines for listings */ 63 static int column; /* current column, limited by 'width' */ 64 static int oldcol; /* last value of column before wrap */ 65 static bool pretty; /* true if we format if-then-else strings */ 66 static bool wrapped; /* true if we wrap too-long strings */ 67 static bool did_wrap; /* true if last wrap_concat did wrapping */ 68 static bool checking; /* true if we are checking for tic */ 69 static int quickdump; /* true if we are dumping compiled data */ 70 71 static char *save_sgr; 72 73 static DYNBUF outbuf; 74 static DYNBUF tmpbuf; 75 76 /* indirection pointers for implementing sort and display modes */ 77 static const PredIdx *bool_indirect, *num_indirect, *str_indirect; 78 static NCURSES_CONST char *const *bool_names; 79 static NCURSES_CONST char *const *num_names; 80 static NCURSES_CONST char *const *str_names; 81 82 static const char *separator = "", *trailer = ""; 83 static int indent = 8; 84 85 /* cover various ports and variants of terminfo */ 86 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */ 87 #define V_SVR1 1 /* SVR1, Ultrix */ 88 #define V_HPUX 2 /* HP/UX */ 89 #define V_AIX 3 /* AIX */ 90 #define V_BSD 4 /* BSD */ 91 92 #if NCURSES_XNAMES 93 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T')) 94 #else 95 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T') 96 #endif 97 98 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && (sortmode != S_VARIABLE) && OBSOLETE(n)) 99 100 #if NCURSES_XNAMES 101 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j])) 102 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j])) 103 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j])) 104 #else 105 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j]) 106 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j]) 107 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j]) 108 #endif 109 110 static void failed(const char *) GCC_NORETURN; 111 112 static void 113 failed(const char *s) 114 { 115 perror(s); 116 ExitProgram(EXIT_FAILURE); 117 } 118 119 static void 120 strncpy_DYN(DYNBUF * dst, const char *src, size_t need) 121 { 122 size_t want = need + dst->used + 1; 123 if (want > dst->size) { 124 dst->size += (want + 1024); /* be generous */ 125 dst->text = typeRealloc(char, dst->size, dst->text); 126 if (dst->text == 0) 127 failed("strncpy_DYN"); 128 } 129 _nc_STRNCPY(dst->text + dst->used, src, need + 1); 130 dst->used += need; 131 dst->text[dst->used] = 0; 132 } 133 134 static void 135 strcpy_DYN(DYNBUF * dst, const char *src) 136 { 137 if (src == 0) { 138 dst->used = 0; 139 strcpy_DYN(dst, ""); 140 } else { 141 strncpy_DYN(dst, src, strlen(src)); 142 } 143 } 144 145 #if NO_LEAKS 146 static void 147 free_DYN(DYNBUF * p) 148 { 149 if (p->text != 0) 150 free(p->text); 151 p->text = 0; 152 p->size = 0; 153 p->used = 0; 154 } 155 156 void 157 _nc_leaks_dump_entry(void) 158 { 159 free_DYN(&outbuf); 160 free_DYN(&tmpbuf); 161 } 162 #endif 163 164 #define NameTrans(check,result) \ 165 if ((np->nte_index <= OK_ ## check) \ 166 && check[np->nte_index]) \ 167 return (result[np->nte_index]) 168 169 NCURSES_CONST char * 170 nametrans(const char *name) 171 /* translate a capability name to termcap from terminfo */ 172 { 173 const struct name_table_entry *np; 174 175 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) { 176 switch (np->nte_type) { 177 case BOOLEAN: 178 NameTrans(bool_from_termcap, boolcodes); 179 break; 180 181 case NUMBER: 182 NameTrans(num_from_termcap, numcodes); 183 break; 184 185 case STRING: 186 NameTrans(str_from_termcap, strcodes); 187 break; 188 } 189 } 190 191 return (0); 192 } 193 194 void 195 dump_init(const char *version, 196 int mode, 197 int sort, 198 bool wrap_strings, 199 int twidth, 200 int theight, 201 unsigned traceval, 202 bool formatted, 203 bool check, 204 int quick) 205 /* set up for entry display */ 206 { 207 width = twidth; 208 height = theight; 209 pretty = formatted; 210 wrapped = wrap_strings; 211 checking = check; 212 quickdump = (quick & 3); 213 214 did_wrap = (width <= 0); 215 216 /* versions */ 217 if (version == 0) 218 tversion = V_ALLCAPS; 219 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1") 220 || !strcmp(version, "Ultrix")) 221 tversion = V_SVR1; 222 else if (!strcmp(version, "HP")) 223 tversion = V_HPUX; 224 else if (!strcmp(version, "AIX")) 225 tversion = V_AIX; 226 else if (!strcmp(version, "BSD")) 227 tversion = V_BSD; 228 else 229 tversion = V_ALLCAPS; 230 231 /* implement display modes */ 232 switch (outform = mode) { 233 case F_LITERAL: 234 case F_TERMINFO: 235 bool_names = boolnames; 236 num_names = numnames; 237 str_names = strnames; 238 separator = (twidth > 0 && theight > 1) ? ", " : ","; 239 trailer = "\n\t"; 240 break; 241 242 case F_VARIABLE: 243 bool_names = boolfnames; 244 num_names = numfnames; 245 str_names = strfnames; 246 separator = (twidth > 0 && theight > 1) ? ", " : ","; 247 trailer = "\n\t"; 248 break; 249 250 case F_TERMCAP: 251 case F_TCONVERR: 252 bool_names = boolcodes; 253 num_names = numcodes; 254 str_names = strcodes; 255 separator = ":"; 256 trailer = "\\\n\t:"; 257 break; 258 } 259 indent = 8; 260 261 /* implement sort modes */ 262 switch (sortmode = sort) { 263 case S_NOSORT: 264 if (traceval) 265 (void) fprintf(stderr, 266 "%s: sorting by term structure order\n", _nc_progname); 267 break; 268 269 case S_TERMINFO: 270 if (traceval) 271 (void) fprintf(stderr, 272 "%s: sorting by terminfo name order\n", _nc_progname); 273 bool_indirect = bool_terminfo_sort; 274 num_indirect = num_terminfo_sort; 275 str_indirect = str_terminfo_sort; 276 break; 277 278 case S_VARIABLE: 279 if (traceval) 280 (void) fprintf(stderr, 281 "%s: sorting by C variable order\n", _nc_progname); 282 bool_indirect = bool_variable_sort; 283 num_indirect = num_variable_sort; 284 str_indirect = str_variable_sort; 285 break; 286 287 case S_TERMCAP: 288 if (traceval) 289 (void) fprintf(stderr, 290 "%s: sorting by termcap name order\n", _nc_progname); 291 bool_indirect = bool_termcap_sort; 292 num_indirect = num_termcap_sort; 293 str_indirect = str_termcap_sort; 294 break; 295 } 296 297 if (traceval) 298 (void) fprintf(stderr, 299 "%s: width = %d, tversion = %d, outform = %d\n", 300 _nc_progname, width, tversion, outform); 301 } 302 303 static TERMTYPE2 *cur_type; 304 305 static int 306 dump_predicate(PredType type, PredIdx idx) 307 /* predicate function to use for ordinary decompilation */ 308 { 309 switch (type) { 310 case BOOLEAN: 311 return (cur_type->Booleans[idx] == FALSE) 312 ? FAIL : cur_type->Booleans[idx]; 313 314 case NUMBER: 315 return (cur_type->Numbers[idx] == ABSENT_NUMERIC) 316 ? FAIL : cur_type->Numbers[idx]; 317 318 case STRING: 319 return (cur_type->Strings[idx] != ABSENT_STRING) 320 ? (int) TRUE : FAIL; 321 } 322 323 return (FALSE); /* pacify compiler */ 324 } 325 326 static void set_obsolete_termcaps(TERMTYPE2 *tp); 327 328 /* is this the index of a function key string? */ 329 #define FNKEY(i) \ 330 (((i) >= STR_IDX(key_f0) && \ 331 (i) <= STR_IDX(key_f9)) || \ 332 ((i) >= STR_IDX(key_f11) && \ 333 (i) <= STR_IDX(key_f63))) 334 335 /* 336 * If we configure with a different Caps file, the offsets into the arrays 337 * will change. So we use an address expression. 338 */ 339 #define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0])) 340 #define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0])) 341 #define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0])) 342 343 static bool 344 version_filter(PredType type, PredIdx idx) 345 /* filter out capabilities we may want to suppress */ 346 { 347 switch (tversion) { 348 case V_ALLCAPS: /* SVr4, XSI Curses */ 349 return (TRUE); 350 351 case V_SVR1: /* System V Release 1, Ultrix */ 352 switch (type) { 353 case BOOLEAN: 354 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); 355 case NUMBER: 356 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); 357 case STRING: 358 return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE); 359 } 360 break; 361 362 case V_HPUX: /* Hewlett-Packard */ 363 switch (type) { 364 case BOOLEAN: 365 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); 366 case NUMBER: 367 return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE); 368 case STRING: 369 if (idx <= STR_IDX(prtr_non)) 370 return (TRUE); 371 else if (FNKEY(idx)) /* function keys */ 372 return (TRUE); 373 else if (idx == STR_IDX(plab_norm) 374 || idx == STR_IDX(label_on) 375 || idx == STR_IDX(label_off)) 376 return (TRUE); 377 else 378 return (FALSE); 379 } 380 break; 381 382 case V_AIX: /* AIX */ 383 switch (type) { 384 case BOOLEAN: 385 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); 386 case NUMBER: 387 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); 388 case STRING: 389 if (idx <= STR_IDX(prtr_non)) 390 return (TRUE); 391 else if (FNKEY(idx)) /* function keys */ 392 return (TRUE); 393 else 394 return (FALSE); 395 } 396 break; 397 398 #define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \ 399 type##_from_termcap[idx]) 400 401 case V_BSD: /* BSD */ 402 switch (type) { 403 case BOOLEAN: 404 return is_termcap(bool); 405 case NUMBER: 406 return is_termcap(num); 407 case STRING: 408 return is_termcap(str); 409 } 410 break; 411 } 412 413 return (FALSE); /* pacify the compiler */ 414 } 415 416 static void 417 trim_trailing(void) 418 { 419 while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ') 420 outbuf.text[--outbuf.used] = '\0'; 421 } 422 423 static void 424 force_wrap(void) 425 { 426 oldcol = column; 427 trim_trailing(); 428 strcpy_DYN(&outbuf, trailer); 429 column = indent; 430 } 431 432 static int 433 op_length(const char *src, int offset) 434 { 435 int result = 0; 436 int ch; 437 if (offset > 0 && src[offset - 1] == '\\') { 438 result = 0; 439 } else { 440 result++; /* for '%' mark */ 441 ch = src[offset + result]; 442 if (TcOutput()) { 443 if (ch == '>') { 444 result += 3; 445 } else if (ch == '+') { 446 result += 2; 447 } else { 448 result++; 449 } 450 } else if (ch == '\'') { 451 result += 3; 452 } else if (ch == L_CURL[0]) { 453 int n = result; 454 while ((ch = src[offset + n]) != '\0') { 455 if (ch == R_CURL[0]) { 456 result = ++n; 457 break; 458 } 459 n++; 460 } 461 } else if (strchr("pPg", ch) != 0) { 462 result += 2; 463 } else { 464 result++; /* ordinary operator */ 465 } 466 } 467 return result; 468 } 469 470 /* 471 * When wrapping too-long strings, avoid splitting a backslash sequence, or 472 * a terminfo '%' operator. That will leave things a little ragged, but avoids 473 * a stray backslash at the end of the line, as well as making the result a 474 * little more readable. 475 */ 476 static int 477 find_split(const char *src, int step, int size) 478 { 479 int result = size; 480 int n; 481 if (size > 0) { 482 /* check if that would split a backslash-sequence */ 483 int mark = size; 484 for (n = size - 1; n > 0; --n) { 485 int ch = UChar(src[step + n]); 486 if (ch == '\\') { 487 if (n > 0 && src[step + n - 1] == ch) 488 --n; 489 mark = n; 490 break; 491 } else if (!isalnum(ch)) { 492 break; 493 } 494 } 495 if (mark < size) { 496 result = mark; 497 } else { 498 /* check if that would split a backslash-sequence */ 499 for (n = size - 1; n > 0; --n) { 500 int ch = UChar(src[step + n]); 501 if (ch == '%') { 502 int need = op_length(src, step + n); 503 if ((n + need) > size) { 504 mark = n; 505 } 506 break; 507 } 508 } 509 if (mark < size) { 510 result = mark; 511 } 512 } 513 } 514 return result; 515 } 516 517 /* 518 * If we are going to wrap lines, we cannot leave literal spaces because that 519 * would be ambiguous if we split on that space. 520 */ 521 static char * 522 fill_spaces(const char *src) 523 { 524 const char *fill = "\\s"; 525 size_t need = strlen(src); 526 size_t size = strlen(fill); 527 char *result = 0; 528 int pass; 529 int s, d; 530 for (pass = 0; pass < 2; ++pass) { 531 for (s = d = 0; src[s] != '\0'; ++s) { 532 if (src[s] == ' ') { 533 if (pass) { 534 _nc_STRCPY(&result[d], fill, need + 1 - d); 535 d += (int) size; 536 } else { 537 need += size; 538 } 539 } else { 540 if (pass) { 541 result[d++] = src[s]; 542 } else { 543 ++d; 544 } 545 } 546 } 547 if (pass) { 548 result[d] = '\0'; 549 } else { 550 result = malloc(need + 1); 551 if (result == 0) 552 failed("fill_spaces"); 553 } 554 } 555 return result; 556 } 557 558 typedef enum { 559 wOFF = 0 560 ,w1ST = 1 561 ,w2ND = 2 562 ,wEND = 4 563 ,wERR = 8 564 } WRAPMODE; 565 566 #define wrap_1ST(mode) ((mode)&w1ST) 567 #define wrap_END(mode) ((mode)&wEND) 568 #define wrap_ERR(mode) ((mode)&wERR) 569 570 static void 571 wrap_concat(const char *src, int need, unsigned mode) 572 { 573 int gaps = (int) strlen(separator); 574 int want = gaps + need; 575 576 did_wrap = (width <= 0); 577 if (wrap_1ST(mode) 578 && column > indent 579 && column + want > width) { 580 force_wrap(); 581 } 582 if ((wrap_END(mode) && !wrap_ERR(mode)) && 583 wrapped && 584 (width >= 0) && 585 (column + want) > width) { 586 int step = 0; 587 int used = width > WRAPPED ? width : WRAPPED; 588 int size; 589 int base = 0; 590 char *p, align[9]; 591 const char *my_t = trailer; 592 char *fill = fill_spaces(src); 593 int last = (int) strlen(fill); 594 595 need = last; 596 597 if (TcOutput()) 598 trailer = "\\\n\t "; 599 600 if (!TcOutput() && (p = strchr(fill, '=')) != 0) { 601 base = (int) (p + 1 - fill); 602 if (base > 8) 603 base = 8; 604 _nc_SPRINTF(align, _nc_SLIMIT(align) "%*s", base, " "); 605 } else if (column > 8) { 606 base = column - 8; 607 if (base > 8) 608 base = 8; 609 _nc_SPRINTF(align, _nc_SLIMIT(align) "%*s", base, " "); 610 } else { 611 align[base] = '\0'; 612 } 613 /* "pretty" overrides wrapping if it already split the line */ 614 if (!pretty || strchr(fill, '\n') == 0) { 615 int tag = 0; 616 617 if (TcOutput() && outbuf.used && !wrap_1ST(mode)) { 618 tag = 3; 619 } 620 621 while ((column + (need + gaps)) > used) { 622 size = used - tag; 623 if (step) { 624 strcpy_DYN(&outbuf, align); 625 size -= base; 626 } 627 if (size > (last - step)) { 628 size = (last - step); 629 } 630 size = find_split(fill, step, size); 631 strncpy_DYN(&outbuf, fill + step, (size_t) size); 632 step += size; 633 need -= size; 634 if (need > 0) { 635 force_wrap(); 636 did_wrap = TRUE; 637 tag = 0; 638 } 639 } 640 } 641 if (need > 0) { 642 if (step) 643 strcpy_DYN(&outbuf, align); 644 strcpy_DYN(&outbuf, fill + step); 645 } 646 if (wrap_END(mode)) 647 strcpy_DYN(&outbuf, separator); 648 trailer = my_t; 649 force_wrap(); 650 651 free(fill); 652 } else { 653 strcpy_DYN(&outbuf, src); 654 if (wrap_END(mode)) 655 strcpy_DYN(&outbuf, separator); 656 column += (int) strlen(src); 657 } 658 } 659 660 static void 661 wrap_concat1(const char *src) 662 { 663 int need = (int) strlen(src); 664 wrap_concat(src, need, w1ST | wEND); 665 } 666 667 static void 668 wrap_concat3(const char *name, const char *eqls, const char *value) 669 { 670 int nlen = (int) strlen(name); 671 int elen = (int) strlen(eqls); 672 int vlen = (int) strlen(value); 673 674 wrap_concat(name, nlen + elen + vlen, w1ST); 675 wrap_concat(eqls, elen + vlen, w2ND); 676 wrap_concat(value, vlen, wEND); 677 } 678 679 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \ 680 if ((size_t)(last - first) > sizeof(sep_trail)-1 \ 681 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \ 682 first += sizeof(sep_trail)-2 683 684 /* Returns the nominal length of the buffer assuming it is termcap format, 685 * i.e., the continuation sequence is treated as a single character ":". 686 * 687 * There are several implementations of termcap which read the text into a 688 * fixed-size buffer. Generally they strip the newlines from the text, but may 689 * not do it until after the buffer is read. Also, "tc=" resolution may be 690 * expanded in the same buffer. This function is useful for measuring the size 691 * of the best fixed-buffer implementation; the worst case may be much worse. 692 */ 693 #ifdef TEST_TERMCAP_LENGTH 694 static int 695 termcap_length(const char *src) 696 { 697 static const char pattern[] = ":\\\n\t:"; 698 699 int len = 0; 700 const char *const t = src + strlen(src); 701 702 while (*src != '\0') { 703 IGNORE_SEP_TRAIL(src, t, pattern); 704 src++; 705 len++; 706 } 707 return len; 708 } 709 #else 710 #define termcap_length(src) strlen(src) 711 #endif 712 713 static void 714 indent_DYN(DYNBUF * buffer, int level) 715 { 716 int n; 717 718 for (n = 0; n < level; n++) 719 strncpy_DYN(buffer, "\t", (size_t) 1); 720 } 721 722 /* 723 * Check if the current line which was begun consists only of a tab and the 724 * given leading text. 725 */ 726 static bool 727 leading_DYN(DYNBUF * buffer, const char *leading) 728 { 729 bool result = FALSE; 730 size_t need = strlen(leading); 731 if (buffer->used > need) { 732 need = buffer->used - need; 733 if (!strcmp(buffer->text + need, leading)) { 734 result = TRUE; 735 while (--need != 0) { 736 if (buffer->text[need] == '\n') { 737 break; 738 } 739 if (buffer->text[need] != '\t') { 740 result = FALSE; 741 break; 742 } 743 } 744 } 745 } 746 return result; 747 } 748 749 bool 750 has_params(const char *src) 751 { 752 bool result = FALSE; 753 int len = (int) strlen(src); 754 int n; 755 bool ifthen = FALSE; 756 bool params = FALSE; 757 758 for (n = 0; n < len - 1; ++n) { 759 if (!strncmp(src + n, "%p", (size_t) 2)) { 760 params = TRUE; 761 } else if (!strncmp(src + n, "%;", (size_t) 2)) { 762 ifthen = TRUE; 763 result = params; 764 break; 765 } 766 } 767 if (!ifthen) { 768 result = ((len > 50) && params); 769 } 770 return result; 771 } 772 773 static char * 774 fmt_complex(TERMTYPE2 *tterm, const char *capability, char *src, int level) 775 { 776 bool percent = FALSE; 777 bool params = has_params(src); 778 779 while (*src != '\0') { 780 switch (*src) { 781 case '^': 782 percent = FALSE; 783 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 784 break; 785 case '\\': 786 percent = FALSE; 787 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 788 break; 789 case '%': 790 percent = TRUE; 791 break; 792 case '?': /* "if" */ 793 case 't': /* "then" */ 794 case 'e': /* "else" */ 795 if (percent) { 796 percent = FALSE; 797 tmpbuf.text[tmpbuf.used - 1] = '\n'; 798 /* treat a "%e" as else-if, on the same level */ 799 if (*src == 'e') { 800 indent_DYN(&tmpbuf, level); 801 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 802 strncpy_DYN(&tmpbuf, src, (size_t) 1); 803 src++; 804 params = has_params(src); 805 if (!params && *src != '\0' && *src != '%') { 806 strncpy_DYN(&tmpbuf, "\n", (size_t) 1); 807 indent_DYN(&tmpbuf, level + 1); 808 } 809 } else { 810 indent_DYN(&tmpbuf, level + 1); 811 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 812 strncpy_DYN(&tmpbuf, src, (size_t) 1); 813 if (*src++ == '?') { 814 src = fmt_complex(tterm, capability, src, level + 1); 815 if (*src != '\0' && *src != '%') { 816 strncpy_DYN(&tmpbuf, "\n", (size_t) 1); 817 indent_DYN(&tmpbuf, level + 1); 818 } 819 } else if (level == 1) { 820 if (checking) 821 _nc_warning("%s: %%%c without %%? in %s", 822 _nc_first_name(tterm->term_names), 823 *src, capability); 824 } 825 } 826 continue; 827 } 828 break; 829 case ';': /* "endif" */ 830 if (percent) { 831 percent = FALSE; 832 if (level > 1) { 833 tmpbuf.text[tmpbuf.used - 1] = '\n'; 834 indent_DYN(&tmpbuf, level); 835 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 836 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 837 if (src[0] == '%' 838 && src[1] != '\0' 839 && (strchr("?e;", src[1])) == 0) { 840 tmpbuf.text[tmpbuf.used++] = '\n'; 841 indent_DYN(&tmpbuf, level); 842 } 843 return src; 844 } 845 if (checking) 846 _nc_warning("%s: %%; without %%? in %s", 847 _nc_first_name(tterm->term_names), 848 capability); 849 } 850 break; 851 case 'p': 852 if (percent && params && !leading_DYN(&tmpbuf, "%")) { 853 tmpbuf.text[tmpbuf.used - 1] = '\n'; 854 indent_DYN(&tmpbuf, level + 1); 855 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 856 } 857 params = FALSE; 858 percent = FALSE; 859 break; 860 case ' ': 861 strncpy_DYN(&tmpbuf, "\\s", (size_t) 2); 862 ++src; 863 continue; 864 default: 865 percent = FALSE; 866 break; 867 } 868 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 869 } 870 return src; 871 } 872 873 /* 874 * Make "large" numbers a little easier to read by showing them in hexadecimal 875 * if they are "close" to a power of two. 876 */ 877 static const char * 878 number_format(int value) 879 { 880 const char *result = "%d"; 881 if ((outform != F_TERMCAP) && (value > 255)) { 882 unsigned long lv = (unsigned long) value; 883 unsigned long mm; 884 int bits = sizeof(unsigned long) * 8; 885 int nn; 886 for (nn = 8; nn < bits; ++nn) { 887 mm = 1UL << nn; 888 if ((mm - 16) <= lv && (mm + 16) > lv) { 889 result = "%#x"; 890 break; 891 } 892 } 893 } 894 return result; 895 } 896 897 #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap) 898 #define EXTRA_CAP 20 899 900 int 901 fmt_entry(TERMTYPE2 *tterm, 902 PredFunc pred, 903 int content_only, 904 int suppress_untranslatable, 905 int infodump, 906 int numbers) 907 { 908 PredIdx i, j; 909 char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP]; 910 char *capability; 911 NCURSES_CONST char *name; 912 int predval, len; 913 PredIdx num_bools = 0; 914 PredIdx num_values = 0; 915 PredIdx num_strings = 0; 916 bool outcount = 0; 917 918 #define WRAP_CONCAT1(s) wrap_concat1(s); outcount = TRUE 919 #define WRAP_CONCAT WRAP_CONCAT1(buffer) 920 921 len = 12; /* terminfo file-header */ 922 923 if (pred == 0) { 924 cur_type = tterm; 925 pred = dump_predicate; 926 } 927 928 strcpy_DYN(&outbuf, 0); 929 if (content_only) { 930 column = indent; /* FIXME: workaround to prevent empty lines */ 931 } else { 932 strcpy_DYN(&outbuf, tterm->term_names); 933 934 /* 935 * Colon is legal in terminfo descriptions, but not in termcap. 936 */ 937 if (!infodump) { 938 char *p = outbuf.text; 939 while (*p) { 940 if (*p == ':') { 941 *p = '='; 942 } 943 ++p; 944 } 945 } 946 strcpy_DYN(&outbuf, separator); 947 column = (int) outbuf.used; 948 if (height > 1) 949 force_wrap(); 950 } 951 952 for_each_boolean(j, tterm) { 953 i = BoolIndirect(j); 954 name = ExtBoolname(tterm, (int) i, bool_names); 955 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 956 957 if (!version_filter(BOOLEAN, i)) 958 continue; 959 else if (isObsolete(outform, name)) 960 continue; 961 962 predval = pred(BOOLEAN, i); 963 if (predval != FAIL) { 964 _nc_STRCPY(buffer, name, sizeof(buffer)); 965 if (predval <= 0) 966 _nc_STRCAT(buffer, "@", sizeof(buffer)); 967 else if (i + 1 > num_bools) 968 num_bools = i + 1; 969 WRAP_CONCAT; 970 } 971 } 972 973 if (column != indent && height > 1) 974 force_wrap(); 975 976 for_each_number(j, tterm) { 977 i = NumIndirect(j); 978 name = ExtNumname(tterm, (int) i, num_names); 979 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 980 981 if (!version_filter(NUMBER, i)) 982 continue; 983 else if (isObsolete(outform, name)) 984 continue; 985 986 predval = pred(NUMBER, i); 987 if (predval != FAIL) { 988 if (tterm->Numbers[i] < 0) { 989 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 990 "%s@", name); 991 } else { 992 size_t nn; 993 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 994 "%s#", name); 995 nn = strlen(buffer); 996 _nc_SPRINTF(buffer + nn, _nc_SLIMIT(sizeof(buffer) - nn) 997 number_format(tterm->Numbers[i]), 998 tterm->Numbers[i]); 999 if (i + 1 > num_values) 1000 num_values = i + 1; 1001 } 1002 WRAP_CONCAT; 1003 } 1004 } 1005 1006 if (column != indent && height > 1) 1007 force_wrap(); 1008 1009 len += (int) (num_bools 1010 + num_values * 2 1011 + strlen(tterm->term_names) + 1); 1012 if (len & 1) 1013 len++; 1014 1015 #undef CUR 1016 #define CUR tterm-> 1017 if (outform == F_TERMCAP) { 1018 if (VALID_STRING(termcap_reset)) { 1019 if (VALID_STRING(init_3string) 1020 && !strcmp(init_3string, termcap_reset)) 1021 DISCARD(init_3string); 1022 1023 if (VALID_STRING(reset_2string) 1024 && !strcmp(reset_2string, termcap_reset)) 1025 DISCARD(reset_2string); 1026 } 1027 } 1028 1029 for_each_string(j, tterm) { 1030 i = StrIndirect(j); 1031 name = ExtStrname(tterm, (int) i, str_names); 1032 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 1033 1034 capability = tterm->Strings[i]; 1035 1036 if (!version_filter(STRING, i)) 1037 continue; 1038 else if (isObsolete(outform, name)) 1039 continue; 1040 1041 #if NCURSES_XNAMES 1042 /* 1043 * Extended names can be longer than 2 characters, but termcap programs 1044 * cannot read those (filter them out). 1045 */ 1046 if (outform == F_TERMCAP && (strlen(name) > 2)) 1047 continue; 1048 #endif 1049 1050 if (outform == F_TERMCAP) { 1051 /* 1052 * Some older versions of vi want rmir/smir to be defined 1053 * for ich/ich1 to work. If they're not defined, force 1054 * them to be output as defined and empty. 1055 */ 1056 if (PRESENT(insert_character) || PRESENT(parm_ich)) { 1057 if (SAME_CAP(i, enter_insert_mode) 1058 && enter_insert_mode == ABSENT_STRING) { 1059 _nc_STRCPY(buffer, "im=", sizeof(buffer)); 1060 WRAP_CONCAT; 1061 continue; 1062 } 1063 1064 if (SAME_CAP(i, exit_insert_mode) 1065 && exit_insert_mode == ABSENT_STRING) { 1066 _nc_STRCPY(buffer, "ei=", sizeof(buffer)); 1067 WRAP_CONCAT; 1068 continue; 1069 } 1070 } 1071 /* 1072 * termcap applications such as screen will be confused if sgr0 1073 * is translated to a string containing rmacs. Filter that out. 1074 */ 1075 if (PRESENT(exit_attribute_mode)) { 1076 if (SAME_CAP(i, exit_attribute_mode)) { 1077 char *trimmed_sgr0; 1078 char *my_sgr = set_attributes; 1079 1080 set_attributes = save_sgr; 1081 1082 trimmed_sgr0 = _nc_trim_sgr0(tterm); 1083 if (strcmp(capability, trimmed_sgr0)) { 1084 capability = trimmed_sgr0; 1085 } else { 1086 if (trimmed_sgr0 != exit_attribute_mode) 1087 free(trimmed_sgr0); 1088 } 1089 1090 set_attributes = my_sgr; 1091 } 1092 } 1093 } 1094 1095 predval = pred(STRING, i); 1096 buffer[0] = '\0'; 1097 1098 if (predval != FAIL) { 1099 if (VALID_STRING(capability) 1100 && i + 1 > num_strings) 1101 num_strings = i + 1; 1102 1103 if (!VALID_STRING(capability)) { 1104 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1105 "%s@", name); 1106 WRAP_CONCAT; 1107 } else if (TcOutput()) { 1108 char *srccap = _nc_tic_expand(capability, TRUE, numbers); 1109 int params = ((i < (int) SIZEOF(parametrized)) 1110 ? parametrized[i] 1111 : ((*srccap == 'k') 1112 ? 0 1113 : has_params(srccap))); 1114 char *cv = _nc_infotocap(name, srccap, params); 1115 1116 if (cv == 0) { 1117 if (outform == F_TCONVERR) { 1118 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1119 "%s=!!! %s WILL NOT CONVERT !!!", 1120 name, srccap); 1121 WRAP_CONCAT; 1122 } else if (suppress_untranslatable) { 1123 continue; 1124 } else { 1125 char *s = srccap, *d = buffer; 1126 int need = 3 + (int) strlen(name); 1127 while ((*d = *s++) != 0) { 1128 if ((d - buffer + 1) >= (int) sizeof(buffer)) { 1129 fprintf(stderr, 1130 "%s: value for %s is too long\n", 1131 _nc_progname, 1132 name); 1133 *d = '\0'; 1134 break; 1135 } 1136 if (*d == ':') { 1137 *d++ = '\\'; 1138 *d = ':'; 1139 } else if (*d == '\\') { 1140 if ((*++d = *s++) == '\0') 1141 break; 1142 } 1143 d++; 1144 *d = '\0'; 1145 } 1146 need += (int) (d - buffer); 1147 wrap_concat("..", need, w1ST | wERR); 1148 need -= 2; 1149 wrap_concat(name, need, wOFF | wERR); 1150 need -= (int) strlen(name); 1151 wrap_concat("=", need, w2ND | wERR); 1152 need -= 1; 1153 wrap_concat(buffer, need, wEND | wERR); 1154 outcount = TRUE; 1155 } 1156 } else { 1157 wrap_concat3(name, "=", cv); 1158 } 1159 len += (int) strlen(capability) + 1; 1160 } else { 1161 char *src = _nc_tic_expand(capability, 1162 outform == F_TERMINFO, numbers); 1163 1164 strcpy_DYN(&tmpbuf, 0); 1165 strcpy_DYN(&tmpbuf, name); 1166 strcpy_DYN(&tmpbuf, "="); 1167 if (pretty 1168 && (outform == F_TERMINFO 1169 || outform == F_VARIABLE)) { 1170 fmt_complex(tterm, name, src, 1); 1171 } else { 1172 strcpy_DYN(&tmpbuf, src); 1173 } 1174 len += (int) strlen(capability) + 1; 1175 WRAP_CONCAT1(tmpbuf.text); 1176 } 1177 } 1178 /* e.g., trimmed_sgr0 */ 1179 if (VALID_STRING(capability) && 1180 capability != tterm->Strings[i]) 1181 free(capability); 1182 } 1183 len += (int) (num_strings * 2); 1184 1185 /* 1186 * This piece of code should be an effective inverse of the functions 1187 * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c. 1188 * Much more work should be done on this to support dumping termcaps. 1189 */ 1190 if (tversion == V_HPUX) { 1191 if (VALID_STRING(memory_lock)) { 1192 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1193 "meml=%s", memory_lock); 1194 WRAP_CONCAT; 1195 } 1196 if (VALID_STRING(memory_unlock)) { 1197 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1198 "memu=%s", memory_unlock); 1199 WRAP_CONCAT; 1200 } 1201 } else if (tversion == V_AIX) { 1202 if (VALID_STRING(acs_chars)) { 1203 bool box_ok = TRUE; 1204 const char *acstrans = "lqkxjmwuvtn"; 1205 const char *cp; 1206 char *tp, *sp, boxchars[11]; 1207 1208 tp = boxchars; 1209 for (cp = acstrans; *cp; cp++) { 1210 sp = (strchr) (acs_chars, *cp); 1211 if (sp) 1212 *tp++ = sp[1]; 1213 else { 1214 box_ok = FALSE; 1215 break; 1216 } 1217 } 1218 tp[0] = '\0'; 1219 1220 if (box_ok) { 1221 char *tmp = _nc_tic_expand(boxchars, 1222 (outform == F_TERMINFO), 1223 numbers); 1224 _nc_STRCPY(buffer, "box1=", sizeof(buffer)); 1225 while (*tmp != '\0') { 1226 size_t have = strlen(buffer); 1227 size_t next = strlen(tmp); 1228 size_t want = have + next + 1; 1229 size_t last = next; 1230 char save = '\0'; 1231 1232 /* 1233 * If the expanded string is too long for the buffer, 1234 * chop it off and save the location where we chopped it. 1235 */ 1236 if (want >= sizeof(buffer)) { 1237 save = tmp[last]; 1238 tmp[last] = '\0'; 1239 } 1240 _nc_STRCAT(buffer, tmp, sizeof(buffer)); 1241 1242 /* 1243 * If we chopped the buffer, replace the missing piece and 1244 * shift everything to append the remainder. 1245 */ 1246 if (save != '\0') { 1247 next = 0; 1248 tmp[last] = save; 1249 while ((tmp[next] = tmp[last + next]) != '\0') { 1250 ++next; 1251 } 1252 } else { 1253 break; 1254 } 1255 } 1256 WRAP_CONCAT; 1257 } 1258 } 1259 } 1260 1261 /* 1262 * kludge: trim off trailer to avoid an extra blank line 1263 * in infocmp -u output when there are no string differences 1264 */ 1265 if (outcount) { 1266 bool trimmed = FALSE; 1267 j = (PredIdx) outbuf.used; 1268 if (wrapped && did_wrap) { 1269 /* EMPTY */ ; 1270 } else if (j >= 2 1271 && outbuf.text[j - 1] == '\t' 1272 && outbuf.text[j - 2] == '\n') { 1273 outbuf.used -= 2; 1274 trimmed = TRUE; 1275 } else if (j >= 4 1276 && outbuf.text[j - 1] == ':' 1277 && outbuf.text[j - 2] == '\t' 1278 && outbuf.text[j - 3] == '\n' 1279 && outbuf.text[j - 4] == '\\') { 1280 outbuf.used -= 4; 1281 trimmed = TRUE; 1282 } 1283 if (trimmed) { 1284 outbuf.text[outbuf.used] = '\0'; 1285 column = oldcol; 1286 strcpy_DYN(&outbuf, " "); 1287 } 1288 } 1289 #if 0 1290 fprintf(stderr, "num_bools = %d\n", num_bools); 1291 fprintf(stderr, "num_values = %d\n", num_values); 1292 fprintf(stderr, "num_strings = %d\n", num_strings); 1293 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", 1294 tterm->term_names, len, outbuf.used, outbuf.text); 1295 #endif 1296 /* 1297 * Here's where we use infodump to trigger a more stringent length check 1298 * for termcap-translation purposes. 1299 * Return the length of the raw entry, without tc= expansions, 1300 * It gives an idea of which entries are deadly to even *scan past*, 1301 * as opposed to *use*. 1302 */ 1303 return (infodump ? len : (int) termcap_length(outbuf.text)); 1304 } 1305 1306 static bool 1307 kill_string(TERMTYPE2 *tterm, char *cap) 1308 { 1309 unsigned n; 1310 for (n = 0; n < NUM_STRINGS(tterm); ++n) { 1311 if (cap == tterm->Strings[n]) { 1312 tterm->Strings[n] = ABSENT_STRING; 1313 return TRUE; 1314 } 1315 } 1316 return FALSE; 1317 } 1318 1319 static char * 1320 find_string(TERMTYPE2 *tterm, char *name) 1321 { 1322 PredIdx n; 1323 for (n = 0; n < NUM_STRINGS(tterm); ++n) { 1324 if (version_filter(STRING, n) 1325 && !strcmp(name, strnames[n])) { 1326 char *cap = tterm->Strings[n]; 1327 if (VALID_STRING(cap)) { 1328 return cap; 1329 } 1330 break; 1331 } 1332 } 1333 return ABSENT_STRING; 1334 } 1335 1336 /* 1337 * This is used to remove function-key labels from a termcap entry to 1338 * make it smaller. 1339 */ 1340 static int 1341 kill_labels(TERMTYPE2 *tterm, int target) 1342 { 1343 int n; 1344 int result = 0; 1345 char *cap; 1346 char name[10]; 1347 1348 for (n = 0; n <= 10; ++n) { 1349 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n); 1350 cap = find_string(tterm, name); 1351 if (VALID_STRING(cap) 1352 && kill_string(tterm, cap)) { 1353 target -= (int) (strlen(cap) + 5); 1354 ++result; 1355 if (target < 0) 1356 break; 1357 } 1358 } 1359 return result; 1360 } 1361 1362 /* 1363 * This is used to remove function-key definitions from a termcap entry to 1364 * make it smaller. 1365 */ 1366 static int 1367 kill_fkeys(TERMTYPE2 *tterm, int target) 1368 { 1369 int n; 1370 int result = 0; 1371 char *cap; 1372 char name[10]; 1373 1374 for (n = 60; n >= 0; --n) { 1375 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n); 1376 cap = find_string(tterm, name); 1377 if (VALID_STRING(cap) 1378 && kill_string(tterm, cap)) { 1379 target -= (int) (strlen(cap) + 5); 1380 ++result; 1381 if (target < 0) 1382 break; 1383 } 1384 } 1385 return result; 1386 } 1387 1388 /* 1389 * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100. 1390 * Also, since this is for termcap, we only care about the line-drawing map. 1391 */ 1392 #define isLine(c) (strchr("lmkjtuvwqxn", c) != 0) 1393 1394 static bool 1395 one_one_mapping(const char *mapping) 1396 { 1397 bool result = TRUE; 1398 1399 if (VALID_STRING(mapping)) { 1400 int n = 0; 1401 while (mapping[n] != '\0' && mapping[n + 1] != '\0') { 1402 if (isLine(mapping[n]) && 1403 mapping[n] != mapping[n + 1]) { 1404 result = FALSE; 1405 break; 1406 } 1407 n += 2; 1408 } 1409 } 1410 return result; 1411 } 1412 1413 #define FMT_ENTRY() \ 1414 fmt_entry(tterm, pred, \ 1415 0, \ 1416 suppress_untranslatable, \ 1417 infodump, numbers) 1418 1419 #define SHOW_WHY PRINTF 1420 1421 static bool 1422 purged_acs(TERMTYPE2 *tterm) 1423 { 1424 bool result = FALSE; 1425 1426 if (VALID_STRING(acs_chars)) { 1427 if (!one_one_mapping(acs_chars)) { 1428 enter_alt_charset_mode = ABSENT_STRING; 1429 exit_alt_charset_mode = ABSENT_STRING; 1430 SHOW_WHY("# (rmacs/smacs removed for consistency)\n"); 1431 } 1432 result = TRUE; 1433 } 1434 return result; 1435 } 1436 1437 static void 1438 encode_b64(char *target, char *source, unsigned state, int *saved) 1439 { 1440 /* RFC-4648 */ 1441 static const char data[] = 1442 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1443 "abcdefghijklmnopqrstuvwxyz" 1444 "0123456789" "-_"; 1445 int ch = UChar(source[state]); 1446 1447 switch (state % 3) { 1448 case 0: 1449 *target++ = data[(ch >> 2) & 077]; 1450 *saved = (ch << 4); 1451 break; 1452 case 1: 1453 *target++ = data[((ch >> 4) | *saved) & 077]; 1454 *saved = (ch << 2); 1455 break; 1456 case 2: 1457 *target++ = data[((ch >> 6) | *saved) & 077]; 1458 *target++ = data[ch & 077]; 1459 *saved = 0; 1460 break; 1461 } 1462 *target = '\0'; 1463 } 1464 1465 /* 1466 * Dump a single entry. 1467 */ 1468 void 1469 dump_entry(TERMTYPE2 *tterm, 1470 int suppress_untranslatable, 1471 int limited, 1472 int numbers, 1473 PredFunc pred) 1474 { 1475 TERMTYPE2 save_tterm; 1476 int len, critlen; 1477 const char *legend; 1478 bool infodump; 1479 1480 if (quickdump) { 1481 char bigbuf[65536]; 1482 unsigned n; 1483 unsigned offset = 0; 1484 separator = ""; 1485 trailer = "\n"; 1486 indent = 0; 1487 if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) { 1488 char numbuf[80]; 1489 if (quickdump & 1) { 1490 if (outbuf.used) 1491 wrap_concat1("\n"); 1492 wrap_concat1("hex:"); 1493 for (n = 0; n < offset; ++n) { 1494 _nc_SPRINTF(numbuf, _nc_SLIMIT(sizeof(numbuf)) 1495 "%02X", UChar(bigbuf[n])); 1496 wrap_concat1(numbuf); 1497 } 1498 } 1499 if (quickdump & 2) { 1500 static char padding[] = 1501 {0, 0}; 1502 int value = 0; 1503 if (outbuf.used) 1504 wrap_concat1("\n"); 1505 wrap_concat1("b64:"); 1506 for (n = 0; n < offset; ++n) { 1507 encode_b64(numbuf, bigbuf, n, &value); 1508 wrap_concat1(numbuf); 1509 } 1510 switch (n % 3) { 1511 case 0: 1512 break; 1513 case 1: 1514 encode_b64(numbuf, padding, 1, &value); 1515 wrap_concat1(numbuf); 1516 wrap_concat1("=="); 1517 break; 1518 case 2: 1519 encode_b64(numbuf, padding, 1, &value); 1520 wrap_concat1(numbuf); 1521 wrap_concat1("="); 1522 break; 1523 } 1524 } 1525 } 1526 return; 1527 } 1528 1529 if (TcOutput()) { 1530 critlen = MAX_TERMCAP_LENGTH; 1531 legend = "older termcap"; 1532 infodump = FALSE; 1533 set_obsolete_termcaps(tterm); 1534 } else { 1535 critlen = MAX_TERMINFO_LENGTH; 1536 legend = "terminfo"; 1537 infodump = TRUE; 1538 } 1539 1540 save_sgr = set_attributes; 1541 1542 if ((FMT_ENTRY() > critlen) 1543 && limited) { 1544 1545 save_tterm = *tterm; 1546 if (!suppress_untranslatable) { 1547 SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n", 1548 critlen); 1549 suppress_untranslatable = TRUE; 1550 } 1551 if (FMT_ENTRY() > critlen) { 1552 /* 1553 * We pick on sgr because it's a nice long string capability that 1554 * is really just an optimization hack. Another good candidate is 1555 * acsc since it is both long and unused by BSD termcap. 1556 */ 1557 bool changed = FALSE; 1558 1559 #if NCURSES_XNAMES 1560 /* 1561 * Extended names are most likely function-key definitions. Drop 1562 * those first. 1563 */ 1564 unsigned n; 1565 for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) { 1566 const char *name = ExtStrname(tterm, (int) n, strnames); 1567 1568 if (VALID_STRING(tterm->Strings[n])) { 1569 set_attributes = ABSENT_STRING; 1570 /* we remove long names anyway - only report the short */ 1571 if (strlen(name) <= 2) { 1572 SHOW_WHY("# (%s removed to fit entry within %d bytes)\n", 1573 name, 1574 critlen); 1575 } 1576 changed = TRUE; 1577 if (FMT_ENTRY() <= critlen) 1578 break; 1579 } 1580 } 1581 #endif 1582 if (VALID_STRING(set_attributes)) { 1583 set_attributes = ABSENT_STRING; 1584 SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n", 1585 critlen); 1586 changed = TRUE; 1587 } 1588 if (!changed || (FMT_ENTRY() > critlen)) { 1589 if (purged_acs(tterm)) { 1590 acs_chars = ABSENT_STRING; 1591 SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n", 1592 critlen); 1593 changed = TRUE; 1594 } 1595 } 1596 if (!changed || (FMT_ENTRY() > critlen)) { 1597 int oldversion = tversion; 1598 1599 tversion = V_BSD; 1600 SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", 1601 critlen); 1602 1603 len = FMT_ENTRY(); 1604 if (len > critlen 1605 && kill_labels(tterm, len - critlen)) { 1606 SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n", 1607 critlen); 1608 len = FMT_ENTRY(); 1609 } 1610 if (len > critlen 1611 && kill_fkeys(tterm, len - critlen)) { 1612 SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n", 1613 critlen); 1614 len = FMT_ENTRY(); 1615 } 1616 if (len > critlen) { 1617 (void) fprintf(stderr, 1618 "%s: %s entry is %d bytes long\n", 1619 _nc_progname, 1620 _nc_first_name(tterm->term_names), 1621 len); 1622 SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", 1623 len, legend); 1624 } 1625 tversion = oldversion; 1626 } 1627 set_attributes = save_sgr; 1628 *tterm = save_tterm; 1629 } 1630 } else if (!version_filter(STRING, STR_IDX(acs_chars))) { 1631 save_tterm = *tterm; 1632 if (purged_acs(tterm)) { 1633 (void) FMT_ENTRY(); 1634 } 1635 *tterm = save_tterm; 1636 } 1637 } 1638 1639 void 1640 dump_uses(const char *name, bool infodump) 1641 /* dump "use=" clauses in the appropriate format */ 1642 { 1643 char buffer[MAX_TERMINFO_LENGTH]; 1644 1645 if (TcOutput()) 1646 trim_trailing(); 1647 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1648 "%s%s", infodump ? "use=" : "tc=", name); 1649 wrap_concat1(buffer); 1650 } 1651 1652 int 1653 show_entry(void) 1654 { 1655 /* 1656 * Trim any remaining whitespace. 1657 */ 1658 if (outbuf.used != 0) { 1659 bool infodump = !TcOutput(); 1660 char delim = (char) (infodump ? ',' : ':'); 1661 int j; 1662 1663 for (j = (int) outbuf.used - 1; j > 0; --j) { 1664 char ch = outbuf.text[j]; 1665 if (ch == '\n') { 1666 ; 1667 } else if (isspace(UChar(ch))) { 1668 outbuf.used = (size_t) j; 1669 } else if (!infodump && ch == '\\') { 1670 outbuf.used = (size_t) j; 1671 } else if (ch == delim && (j == 0 || outbuf.text[j - 1] != '\\')) { 1672 outbuf.used = (size_t) (j + 1); 1673 } else { 1674 break; 1675 } 1676 } 1677 outbuf.text[outbuf.used] = '\0'; 1678 } 1679 if (outbuf.text != 0) { 1680 (void) fputs(outbuf.text, stdout); 1681 putchar('\n'); 1682 } 1683 return (int) outbuf.used; 1684 } 1685 1686 void 1687 compare_entry(PredHook hook, 1688 TERMTYPE2 *tp GCC_UNUSED, 1689 bool quiet) 1690 /* compare two entries */ 1691 { 1692 PredIdx i, j; 1693 NCURSES_CONST char *name; 1694 1695 if (!quiet) 1696 fputs(" comparing booleans.\n", stdout); 1697 for_each_boolean(j, tp) { 1698 i = BoolIndirect(j); 1699 name = ExtBoolname(tp, (int) i, bool_names); 1700 1701 if (isObsolete(outform, name)) 1702 continue; 1703 1704 (*hook) (CMP_BOOLEAN, i, name); 1705 } 1706 1707 if (!quiet) 1708 fputs(" comparing numbers.\n", stdout); 1709 for_each_number(j, tp) { 1710 i = NumIndirect(j); 1711 name = ExtNumname(tp, (int) i, num_names); 1712 1713 if (isObsolete(outform, name)) 1714 continue; 1715 1716 (*hook) (CMP_NUMBER, i, name); 1717 } 1718 1719 if (!quiet) 1720 fputs(" comparing strings.\n", stdout); 1721 for_each_string(j, tp) { 1722 i = StrIndirect(j); 1723 name = ExtStrname(tp, (int) i, str_names); 1724 1725 if (isObsolete(outform, name)) 1726 continue; 1727 1728 (*hook) (CMP_STRING, i, name); 1729 } 1730 1731 /* (void) fputs(" comparing use entries.\n", stdout); */ 1732 (*hook) (CMP_USE, 0, "use"); 1733 1734 } 1735 1736 #define NOTSET(s) ((s) == 0) 1737 1738 /* 1739 * This bit of legerdemain turns all the terminfo variable names into 1740 * references to locations in the arrays Booleans, Numbers, and Strings --- 1741 * precisely what's needed. 1742 */ 1743 #undef CUR 1744 #define CUR tp-> 1745 1746 static void 1747 set_obsolete_termcaps(TERMTYPE2 *tp) 1748 { 1749 #include "capdefaults.c" 1750 } 1751 1752 /* 1753 * Convert an alternate-character-set string to canonical form: sorted and 1754 * unique. 1755 */ 1756 void 1757 repair_acsc(TERMTYPE2 *tp) 1758 { 1759 if (VALID_STRING(acs_chars)) { 1760 size_t n, m; 1761 char mapped[256]; 1762 char extra = 0; 1763 unsigned source; 1764 unsigned target; 1765 bool fix_needed = FALSE; 1766 1767 for (n = 0, source = 0; acs_chars[n] != 0; n++) { 1768 target = UChar(acs_chars[n]); 1769 if (source >= target) { 1770 fix_needed = TRUE; 1771 break; 1772 } 1773 source = target; 1774 if (acs_chars[n + 1]) 1775 n++; 1776 } 1777 if (fix_needed) { 1778 memset(mapped, 0, sizeof(mapped)); 1779 for (n = 0; acs_chars[n] != 0; n++) { 1780 source = UChar(acs_chars[n]); 1781 if ((target = (unsigned char) acs_chars[n + 1]) != 0) { 1782 mapped[source] = (char) target; 1783 n++; 1784 } else { 1785 extra = (char) source; 1786 } 1787 } 1788 for (n = m = 0; n < sizeof(mapped); n++) { 1789 if (mapped[n]) { 1790 acs_chars[m++] = (char) n; 1791 acs_chars[m++] = mapped[n]; 1792 } 1793 } 1794 if (extra) 1795 acs_chars[m++] = extra; /* garbage in, garbage out */ 1796 acs_chars[m] = 0; 1797 } 1798 } 1799 } 1800