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