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.181 2020/12/26 18:25:18 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, bool formatting) 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 if (formatting) { 769 result = ((len > 50) && params); 770 } else { 771 result = params; 772 } 773 } 774 return result; 775 } 776 777 static char * 778 fmt_complex(TERMTYPE2 *tterm, const char *capability, char *src, int level) 779 { 780 bool percent = FALSE; 781 bool params = has_params(src, TRUE); 782 783 while (*src != '\0') { 784 switch (*src) { 785 case '^': 786 percent = FALSE; 787 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 788 break; 789 case '\\': 790 percent = FALSE; 791 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 792 break; 793 case '%': 794 percent = TRUE; 795 break; 796 case '?': /* "if" */ 797 case 't': /* "then" */ 798 case 'e': /* "else" */ 799 if (percent) { 800 percent = FALSE; 801 tmpbuf.text[tmpbuf.used - 1] = '\n'; 802 /* treat a "%e" as else-if, on the same level */ 803 if (*src == 'e') { 804 indent_DYN(&tmpbuf, level); 805 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 806 strncpy_DYN(&tmpbuf, src, (size_t) 1); 807 src++; 808 params = has_params(src, TRUE); 809 if (!params && *src != '\0' && *src != '%') { 810 strncpy_DYN(&tmpbuf, "\n", (size_t) 1); 811 indent_DYN(&tmpbuf, level + 1); 812 } 813 } else { 814 indent_DYN(&tmpbuf, level + 1); 815 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 816 strncpy_DYN(&tmpbuf, src, (size_t) 1); 817 if (*src++ == '?') { 818 src = fmt_complex(tterm, capability, src, level + 1); 819 if (*src != '\0' && *src != '%') { 820 strncpy_DYN(&tmpbuf, "\n", (size_t) 1); 821 indent_DYN(&tmpbuf, level + 1); 822 } 823 } else if (level == 1) { 824 if (checking) 825 _nc_warning("%s: %%%c without %%? in %s", 826 _nc_first_name(tterm->term_names), 827 *src, capability); 828 } 829 } 830 continue; 831 } 832 break; 833 case ';': /* "endif" */ 834 if (percent) { 835 percent = FALSE; 836 if (level > 1) { 837 tmpbuf.text[tmpbuf.used - 1] = '\n'; 838 indent_DYN(&tmpbuf, level); 839 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 840 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 841 if (src[0] == '%' 842 && src[1] != '\0' 843 && (strchr("?e;", src[1])) == 0) { 844 tmpbuf.text[tmpbuf.used++] = '\n'; 845 indent_DYN(&tmpbuf, level); 846 } 847 return src; 848 } 849 if (checking) 850 _nc_warning("%s: %%; without %%? in %s", 851 _nc_first_name(tterm->term_names), 852 capability); 853 } 854 break; 855 case 'p': 856 if (percent && params && !leading_DYN(&tmpbuf, "%")) { 857 tmpbuf.text[tmpbuf.used - 1] = '\n'; 858 indent_DYN(&tmpbuf, level + 1); 859 strncpy_DYN(&tmpbuf, "%", (size_t) 1); 860 } 861 params = FALSE; 862 percent = FALSE; 863 break; 864 case ' ': 865 strncpy_DYN(&tmpbuf, "\\s", (size_t) 2); 866 ++src; 867 continue; 868 default: 869 percent = FALSE; 870 break; 871 } 872 strncpy_DYN(&tmpbuf, src++, (size_t) 1); 873 } 874 return src; 875 } 876 877 /* 878 * Make "large" numbers a little easier to read by showing them in hexadecimal 879 * if they are "close" to a power of two. 880 */ 881 static const char * 882 number_format(int value) 883 { 884 const char *result = "%d"; 885 if ((outform != F_TERMCAP) && (value > 255)) { 886 unsigned long lv = (unsigned long) value; 887 unsigned long mm; 888 int bits = sizeof(unsigned long) * 8; 889 int nn; 890 for (nn = 8; nn < bits; ++nn) { 891 mm = 1UL << nn; 892 if ((mm - 16) <= lv && (mm + 16) > lv) { 893 result = "%#x"; 894 break; 895 } 896 } 897 } 898 return result; 899 } 900 901 #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap) 902 #define EXTRA_CAP 20 903 904 int 905 fmt_entry(TERMTYPE2 *tterm, 906 PredFunc pred, 907 int content_only, 908 int suppress_untranslatable, 909 int infodump, 910 int numbers) 911 { 912 PredIdx i, j; 913 char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP]; 914 char *capability; 915 NCURSES_CONST char *name; 916 int predval, len; 917 PredIdx num_bools = 0; 918 PredIdx num_values = 0; 919 PredIdx num_strings = 0; 920 bool outcount = 0; 921 922 #define WRAP_CONCAT1(s) wrap_concat1(s); outcount = TRUE 923 #define WRAP_CONCAT WRAP_CONCAT1(buffer) 924 925 len = 12; /* terminfo file-header */ 926 927 if (pred == 0) { 928 cur_type = tterm; 929 pred = dump_predicate; 930 } 931 932 strcpy_DYN(&outbuf, 0); 933 if (content_only) { 934 column = indent; /* FIXME: workaround to prevent empty lines */ 935 } else { 936 strcpy_DYN(&outbuf, tterm->term_names); 937 938 /* 939 * Colon is legal in terminfo descriptions, but not in termcap. 940 */ 941 if (!infodump) { 942 char *p = outbuf.text; 943 while (*p) { 944 if (*p == ':') { 945 *p = '='; 946 } 947 ++p; 948 } 949 } 950 strcpy_DYN(&outbuf, separator); 951 column = (int) outbuf.used; 952 if (height > 1) 953 force_wrap(); 954 } 955 956 for_each_boolean(j, tterm) { 957 i = BoolIndirect(j); 958 name = ExtBoolname(tterm, (int) i, bool_names); 959 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 960 961 if (!version_filter(BOOLEAN, i)) 962 continue; 963 else if (isObsolete(outform, name)) 964 continue; 965 966 predval = pred(BOOLEAN, i); 967 if (predval != FAIL) { 968 _nc_STRCPY(buffer, name, sizeof(buffer)); 969 if (predval <= 0) 970 _nc_STRCAT(buffer, "@", sizeof(buffer)); 971 else if (i + 1 > num_bools) 972 num_bools = i + 1; 973 WRAP_CONCAT; 974 } 975 } 976 977 if (column != indent && height > 1) 978 force_wrap(); 979 980 for_each_number(j, tterm) { 981 i = NumIndirect(j); 982 name = ExtNumname(tterm, (int) i, num_names); 983 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 984 985 if (!version_filter(NUMBER, i)) 986 continue; 987 else if (isObsolete(outform, name)) 988 continue; 989 990 predval = pred(NUMBER, i); 991 if (predval != FAIL) { 992 if (tterm->Numbers[i] < 0) { 993 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 994 "%s@", name); 995 } else { 996 size_t nn; 997 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 998 "%s#", name); 999 nn = strlen(buffer); 1000 _nc_SPRINTF(buffer + nn, _nc_SLIMIT(sizeof(buffer) - nn) 1001 number_format(tterm->Numbers[i]), 1002 tterm->Numbers[i]); 1003 if (i + 1 > num_values) 1004 num_values = i + 1; 1005 } 1006 WRAP_CONCAT; 1007 } 1008 } 1009 1010 if (column != indent && height > 1) 1011 force_wrap(); 1012 1013 len += (int) (num_bools 1014 + num_values * 2 1015 + strlen(tterm->term_names) + 1); 1016 if (len & 1) 1017 len++; 1018 1019 #undef CUR 1020 #define CUR tterm-> 1021 if (outform == F_TERMCAP) { 1022 if (VALID_STRING(termcap_reset)) { 1023 if (VALID_STRING(init_3string) 1024 && !strcmp(init_3string, termcap_reset)) 1025 DISCARD(init_3string); 1026 1027 if (VALID_STRING(reset_2string) 1028 && !strcmp(reset_2string, termcap_reset)) 1029 DISCARD(reset_2string); 1030 } 1031 } 1032 1033 for_each_string(j, tterm) { 1034 i = StrIndirect(j); 1035 name = ExtStrname(tterm, (int) i, str_names); 1036 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); 1037 1038 capability = tterm->Strings[i]; 1039 1040 if (!version_filter(STRING, i)) 1041 continue; 1042 else if (isObsolete(outform, name)) 1043 continue; 1044 1045 #if NCURSES_XNAMES 1046 /* 1047 * Extended names can be longer than 2 characters, but termcap programs 1048 * cannot read those (filter them out). 1049 */ 1050 if (outform == F_TERMCAP && (strlen(name) > 2)) 1051 continue; 1052 #endif 1053 1054 if (outform == F_TERMCAP) { 1055 /* 1056 * Some older versions of vi want rmir/smir to be defined 1057 * for ich/ich1 to work. If they're not defined, force 1058 * them to be output as defined and empty. 1059 */ 1060 if (PRESENT(insert_character) || PRESENT(parm_ich)) { 1061 if (SAME_CAP(i, enter_insert_mode) 1062 && enter_insert_mode == ABSENT_STRING) { 1063 _nc_STRCPY(buffer, "im=", sizeof(buffer)); 1064 WRAP_CONCAT; 1065 continue; 1066 } 1067 1068 if (SAME_CAP(i, exit_insert_mode) 1069 && exit_insert_mode == ABSENT_STRING) { 1070 _nc_STRCPY(buffer, "ei=", sizeof(buffer)); 1071 WRAP_CONCAT; 1072 continue; 1073 } 1074 } 1075 /* 1076 * termcap applications such as screen will be confused if sgr0 1077 * is translated to a string containing rmacs. Filter that out. 1078 */ 1079 if (PRESENT(exit_attribute_mode)) { 1080 if (SAME_CAP(i, exit_attribute_mode)) { 1081 char *trimmed_sgr0; 1082 char *my_sgr = set_attributes; 1083 1084 set_attributes = save_sgr; 1085 1086 trimmed_sgr0 = _nc_trim_sgr0(tterm); 1087 if (strcmp(capability, trimmed_sgr0)) { 1088 capability = trimmed_sgr0; 1089 } else { 1090 if (trimmed_sgr0 != exit_attribute_mode) 1091 free(trimmed_sgr0); 1092 } 1093 1094 set_attributes = my_sgr; 1095 } 1096 } 1097 } 1098 1099 predval = pred(STRING, i); 1100 buffer[0] = '\0'; 1101 1102 if (predval != FAIL) { 1103 if (VALID_STRING(capability) 1104 && i + 1 > num_strings) 1105 num_strings = i + 1; 1106 1107 if (!VALID_STRING(capability)) { 1108 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1109 "%s@", name); 1110 WRAP_CONCAT; 1111 } else if (TcOutput()) { 1112 char *srccap = _nc_tic_expand(capability, TRUE, numbers); 1113 int params = ((i < (int) SIZEOF(parametrized)) 1114 ? parametrized[i] 1115 : ((*srccap == 'k') 1116 ? 0 1117 : has_params(srccap, FALSE))); 1118 char *cv = _nc_infotocap(name, srccap, params); 1119 1120 if (cv == 0) { 1121 if (outform == F_TCONVERR) { 1122 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1123 "%s=!!! %s WILL NOT CONVERT !!!", 1124 name, srccap); 1125 WRAP_CONCAT; 1126 } else if (suppress_untranslatable) { 1127 continue; 1128 } else { 1129 char *s = srccap, *d = buffer; 1130 int need = 3 + (int) strlen(name); 1131 while ((*d = *s++) != 0) { 1132 if ((d - buffer + 2) >= (int) sizeof(buffer)) { 1133 fprintf(stderr, 1134 "%s: value for %s is too long\n", 1135 _nc_progname, 1136 name); 1137 *d = '\0'; 1138 break; 1139 } 1140 if (*d == ':') { 1141 *d++ = '\\'; 1142 *d = ':'; 1143 } else if (*d == '\\') { 1144 if ((*++d = *s++) == '\0') 1145 break; 1146 } 1147 d++; 1148 *d = '\0'; 1149 } 1150 need += (int) (d - buffer); 1151 wrap_concat("..", need, w1ST | wERR); 1152 need -= 2; 1153 wrap_concat(name, need, wOFF | wERR); 1154 need -= (int) strlen(name); 1155 wrap_concat("=", need, w2ND | wERR); 1156 need -= 1; 1157 wrap_concat(buffer, need, wEND | wERR); 1158 outcount = TRUE; 1159 } 1160 } else { 1161 wrap_concat3(name, "=", cv); 1162 } 1163 len += (int) strlen(capability) + 1; 1164 } else { 1165 char *src = _nc_tic_expand(capability, 1166 outform == F_TERMINFO, numbers); 1167 1168 strcpy_DYN(&tmpbuf, 0); 1169 strcpy_DYN(&tmpbuf, name); 1170 strcpy_DYN(&tmpbuf, "="); 1171 if (pretty 1172 && (outform == F_TERMINFO 1173 || outform == F_VARIABLE)) { 1174 fmt_complex(tterm, name, src, 1); 1175 } else { 1176 strcpy_DYN(&tmpbuf, src); 1177 } 1178 len += (int) strlen(capability) + 1; 1179 WRAP_CONCAT1(tmpbuf.text); 1180 } 1181 } 1182 /* e.g., trimmed_sgr0 */ 1183 if (VALID_STRING(capability) && 1184 capability != tterm->Strings[i]) 1185 free(capability); 1186 } 1187 len += (int) (num_strings * 2); 1188 1189 /* 1190 * This piece of code should be an effective inverse of the functions 1191 * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c. 1192 * Much more work should be done on this to support dumping termcaps. 1193 */ 1194 if (tversion == V_HPUX) { 1195 if (VALID_STRING(memory_lock)) { 1196 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1197 "meml=%s", memory_lock); 1198 WRAP_CONCAT; 1199 } 1200 if (VALID_STRING(memory_unlock)) { 1201 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1202 "memu=%s", memory_unlock); 1203 WRAP_CONCAT; 1204 } 1205 } else if (tversion == V_AIX) { 1206 if (VALID_STRING(acs_chars)) { 1207 bool box_ok = TRUE; 1208 const char *acstrans = "lqkxjmwuvtn"; 1209 const char *cp; 1210 char *tp, *sp, boxchars[11]; 1211 1212 tp = boxchars; 1213 for (cp = acstrans; *cp; cp++) { 1214 sp = (strchr) (acs_chars, *cp); 1215 if (sp) 1216 *tp++ = sp[1]; 1217 else { 1218 box_ok = FALSE; 1219 break; 1220 } 1221 } 1222 tp[0] = '\0'; 1223 1224 if (box_ok) { 1225 char *tmp = _nc_tic_expand(boxchars, 1226 (outform == F_TERMINFO), 1227 numbers); 1228 _nc_STRCPY(buffer, "box1=", sizeof(buffer)); 1229 while (*tmp != '\0') { 1230 size_t have = strlen(buffer); 1231 size_t next = strlen(tmp); 1232 size_t want = have + next + 1; 1233 size_t last = next; 1234 char save = '\0'; 1235 1236 /* 1237 * If the expanded string is too long for the buffer, 1238 * chop it off and save the location where we chopped it. 1239 */ 1240 if (want >= sizeof(buffer)) { 1241 save = tmp[last]; 1242 tmp[last] = '\0'; 1243 } 1244 _nc_STRCAT(buffer, tmp, sizeof(buffer)); 1245 1246 /* 1247 * If we chopped the buffer, replace the missing piece and 1248 * shift everything to append the remainder. 1249 */ 1250 if (save != '\0') { 1251 next = 0; 1252 tmp[last] = save; 1253 while ((tmp[next] = tmp[last + next]) != '\0') { 1254 ++next; 1255 } 1256 } else { 1257 break; 1258 } 1259 } 1260 WRAP_CONCAT; 1261 } 1262 } 1263 } 1264 1265 /* 1266 * kludge: trim off trailer to avoid an extra blank line 1267 * in infocmp -u output when there are no string differences 1268 */ 1269 if (outcount) { 1270 bool trimmed = FALSE; 1271 j = (PredIdx) outbuf.used; 1272 if (wrapped && did_wrap) { 1273 /* EMPTY */ ; 1274 } else if (j >= 2 1275 && outbuf.text[j - 1] == '\t' 1276 && outbuf.text[j - 2] == '\n') { 1277 outbuf.used -= 2; 1278 trimmed = TRUE; 1279 } else if (j >= 4 1280 && outbuf.text[j - 1] == ':' 1281 && outbuf.text[j - 2] == '\t' 1282 && outbuf.text[j - 3] == '\n' 1283 && outbuf.text[j - 4] == '\\') { 1284 outbuf.used -= 4; 1285 trimmed = TRUE; 1286 } 1287 if (trimmed) { 1288 outbuf.text[outbuf.used] = '\0'; 1289 column = oldcol; 1290 strcpy_DYN(&outbuf, " "); 1291 } 1292 } 1293 #if 0 1294 fprintf(stderr, "num_bools = %d\n", num_bools); 1295 fprintf(stderr, "num_values = %d\n", num_values); 1296 fprintf(stderr, "num_strings = %d\n", num_strings); 1297 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", 1298 tterm->term_names, len, outbuf.used, outbuf.text); 1299 #endif 1300 /* 1301 * Here's where we use infodump to trigger a more stringent length check 1302 * for termcap-translation purposes. 1303 * Return the length of the raw entry, without tc= expansions, 1304 * It gives an idea of which entries are deadly to even *scan past*, 1305 * as opposed to *use*. 1306 */ 1307 return (infodump ? len : (int) termcap_length(outbuf.text)); 1308 } 1309 1310 static bool 1311 kill_string(TERMTYPE2 *tterm, char *cap) 1312 { 1313 unsigned n; 1314 for (n = 0; n < NUM_STRINGS(tterm); ++n) { 1315 if (cap == tterm->Strings[n]) { 1316 tterm->Strings[n] = ABSENT_STRING; 1317 return TRUE; 1318 } 1319 } 1320 return FALSE; 1321 } 1322 1323 static char * 1324 find_string(TERMTYPE2 *tterm, char *name) 1325 { 1326 PredIdx n; 1327 for (n = 0; n < NUM_STRINGS(tterm); ++n) { 1328 if (version_filter(STRING, n) 1329 && !strcmp(name, strnames[n])) { 1330 char *cap = tterm->Strings[n]; 1331 if (VALID_STRING(cap)) { 1332 return cap; 1333 } 1334 break; 1335 } 1336 } 1337 return ABSENT_STRING; 1338 } 1339 1340 /* 1341 * This is used to remove function-key labels from a termcap entry to 1342 * make it smaller. 1343 */ 1344 static int 1345 kill_labels(TERMTYPE2 *tterm, int target) 1346 { 1347 int n; 1348 int result = 0; 1349 char *cap; 1350 char name[20]; 1351 1352 for (n = 0; n <= 10; ++n) { 1353 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n); 1354 cap = find_string(tterm, name); 1355 if (VALID_STRING(cap) 1356 && kill_string(tterm, cap)) { 1357 target -= (int) (strlen(cap) + 5); 1358 ++result; 1359 if (target < 0) 1360 break; 1361 } 1362 } 1363 return result; 1364 } 1365 1366 /* 1367 * This is used to remove function-key definitions from a termcap entry to 1368 * make it smaller. 1369 */ 1370 static int 1371 kill_fkeys(TERMTYPE2 *tterm, int target) 1372 { 1373 int n; 1374 int result = 0; 1375 char *cap; 1376 char name[20]; 1377 1378 for (n = 60; n >= 0; --n) { 1379 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n); 1380 cap = find_string(tterm, name); 1381 if (VALID_STRING(cap) 1382 && kill_string(tterm, cap)) { 1383 target -= (int) (strlen(cap) + 5); 1384 ++result; 1385 if (target < 0) 1386 break; 1387 } 1388 } 1389 return result; 1390 } 1391 1392 /* 1393 * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100. 1394 * Also, since this is for termcap, we only care about the line-drawing map. 1395 */ 1396 #define isLine(c) (strchr("lmkjtuvwqxn", c) != 0) 1397 1398 static bool 1399 one_one_mapping(const char *mapping) 1400 { 1401 bool result = TRUE; 1402 1403 if (VALID_STRING(mapping)) { 1404 int n = 0; 1405 while (mapping[n] != '\0' && mapping[n + 1] != '\0') { 1406 if (isLine(mapping[n]) && 1407 mapping[n] != mapping[n + 1]) { 1408 result = FALSE; 1409 break; 1410 } 1411 n += 2; 1412 } 1413 } 1414 return result; 1415 } 1416 1417 #define FMT_ENTRY() \ 1418 fmt_entry(tterm, pred, \ 1419 0, \ 1420 suppress_untranslatable, \ 1421 infodump, numbers) 1422 1423 #define SHOW_WHY PRINTF 1424 1425 static bool 1426 purged_acs(TERMTYPE2 *tterm) 1427 { 1428 bool result = FALSE; 1429 1430 if (VALID_STRING(acs_chars)) { 1431 if (!one_one_mapping(acs_chars)) { 1432 enter_alt_charset_mode = ABSENT_STRING; 1433 exit_alt_charset_mode = ABSENT_STRING; 1434 SHOW_WHY("# (rmacs/smacs removed for consistency)\n"); 1435 } 1436 result = TRUE; 1437 } 1438 return result; 1439 } 1440 1441 static void 1442 encode_b64(char *target, char *source, unsigned state, int *saved) 1443 { 1444 /* RFC-4648 */ 1445 static const char data[] = 1446 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1447 "abcdefghijklmnopqrstuvwxyz" 1448 "0123456789" "-_"; 1449 int ch = UChar(source[state]); 1450 1451 switch (state % 3) { 1452 case 0: 1453 *target++ = data[(ch >> 2) & 077]; 1454 *saved = (ch << 4); 1455 break; 1456 case 1: 1457 *target++ = data[((ch >> 4) | *saved) & 077]; 1458 *saved = (ch << 2); 1459 break; 1460 case 2: 1461 *target++ = data[((ch >> 6) | *saved) & 077]; 1462 *target++ = data[ch & 077]; 1463 *saved = 0; 1464 break; 1465 } 1466 *target = '\0'; 1467 } 1468 1469 /* 1470 * Dump a single entry. 1471 */ 1472 void 1473 dump_entry(TERMTYPE2 *tterm, 1474 int suppress_untranslatable, 1475 int limited, 1476 int numbers, 1477 PredFunc pred) 1478 { 1479 TERMTYPE2 save_tterm; 1480 int len, critlen; 1481 const char *legend; 1482 bool infodump; 1483 1484 if (quickdump) { 1485 char bigbuf[65536]; 1486 unsigned n; 1487 unsigned offset = 0; 1488 separator = ""; 1489 trailer = "\n"; 1490 indent = 0; 1491 if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) { 1492 char numbuf[80]; 1493 if (quickdump & 1) { 1494 if (outbuf.used) 1495 wrap_concat1("\n"); 1496 wrap_concat1("hex:"); 1497 for (n = 0; n < offset; ++n) { 1498 _nc_SPRINTF(numbuf, _nc_SLIMIT(sizeof(numbuf)) 1499 "%02X", UChar(bigbuf[n])); 1500 wrap_concat1(numbuf); 1501 } 1502 } 1503 if (quickdump & 2) { 1504 static char padding[] = 1505 {0, 0}; 1506 int value = 0; 1507 if (outbuf.used) 1508 wrap_concat1("\n"); 1509 wrap_concat1("b64:"); 1510 for (n = 0; n < offset; ++n) { 1511 encode_b64(numbuf, bigbuf, n, &value); 1512 wrap_concat1(numbuf); 1513 } 1514 switch (n % 3) { 1515 case 0: 1516 break; 1517 case 1: 1518 encode_b64(numbuf, padding, 1, &value); 1519 wrap_concat1(numbuf); 1520 wrap_concat1("=="); 1521 break; 1522 case 2: 1523 encode_b64(numbuf, padding, 1, &value); 1524 wrap_concat1(numbuf); 1525 wrap_concat1("="); 1526 break; 1527 } 1528 } 1529 } 1530 return; 1531 } 1532 1533 if (TcOutput()) { 1534 critlen = MAX_TERMCAP_LENGTH; 1535 legend = "older termcap"; 1536 infodump = FALSE; 1537 set_obsolete_termcaps(tterm); 1538 } else { 1539 critlen = MAX_TERMINFO_LENGTH; 1540 legend = "terminfo"; 1541 infodump = TRUE; 1542 } 1543 1544 save_sgr = set_attributes; 1545 1546 if ((FMT_ENTRY() > critlen) 1547 && limited) { 1548 1549 save_tterm = *tterm; 1550 if (!suppress_untranslatable) { 1551 SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n", 1552 critlen); 1553 suppress_untranslatable = TRUE; 1554 } 1555 if (FMT_ENTRY() > critlen) { 1556 /* 1557 * We pick on sgr because it's a nice long string capability that 1558 * is really just an optimization hack. Another good candidate is 1559 * acsc since it is both long and unused by BSD termcap. 1560 */ 1561 bool changed = FALSE; 1562 1563 #if NCURSES_XNAMES 1564 /* 1565 * Extended names are most likely function-key definitions. Drop 1566 * those first. 1567 */ 1568 unsigned n; 1569 for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) { 1570 const char *name = ExtStrname(tterm, (int) n, strnames); 1571 1572 if (VALID_STRING(tterm->Strings[n])) { 1573 set_attributes = ABSENT_STRING; 1574 /* we remove long names anyway - only report the short */ 1575 if (strlen(name) <= 2) { 1576 SHOW_WHY("# (%s removed to fit entry within %d bytes)\n", 1577 name, 1578 critlen); 1579 } 1580 changed = TRUE; 1581 if (FMT_ENTRY() <= critlen) 1582 break; 1583 } 1584 } 1585 #endif 1586 if (VALID_STRING(set_attributes)) { 1587 set_attributes = ABSENT_STRING; 1588 SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n", 1589 critlen); 1590 changed = TRUE; 1591 } 1592 if (!changed || (FMT_ENTRY() > critlen)) { 1593 if (purged_acs(tterm)) { 1594 acs_chars = ABSENT_STRING; 1595 SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n", 1596 critlen); 1597 changed = TRUE; 1598 } 1599 } 1600 if (!changed || (FMT_ENTRY() > critlen)) { 1601 int oldversion = tversion; 1602 1603 tversion = V_BSD; 1604 SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", 1605 critlen); 1606 1607 len = FMT_ENTRY(); 1608 if (len > critlen 1609 && kill_labels(tterm, len - critlen)) { 1610 SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n", 1611 critlen); 1612 len = FMT_ENTRY(); 1613 } 1614 if (len > critlen 1615 && kill_fkeys(tterm, len - critlen)) { 1616 SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n", 1617 critlen); 1618 len = FMT_ENTRY(); 1619 } 1620 if (len > critlen) { 1621 (void) fprintf(stderr, 1622 "%s: %s entry is %d bytes long\n", 1623 _nc_progname, 1624 _nc_first_name(tterm->term_names), 1625 len); 1626 SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", 1627 len, legend); 1628 } 1629 tversion = oldversion; 1630 } 1631 set_attributes = save_sgr; 1632 *tterm = save_tterm; 1633 } 1634 } else if (!version_filter(STRING, STR_IDX(acs_chars))) { 1635 save_tterm = *tterm; 1636 if (purged_acs(tterm)) { 1637 (void) FMT_ENTRY(); 1638 } 1639 *tterm = save_tterm; 1640 } 1641 } 1642 1643 void 1644 dump_uses(const char *name, bool infodump) 1645 /* dump "use=" clauses in the appropriate format */ 1646 { 1647 char buffer[MAX_TERMINFO_LENGTH]; 1648 1649 if (TcOutput()) 1650 trim_trailing(); 1651 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) 1652 "%s%s", infodump ? "use=" : "tc=", name); 1653 wrap_concat1(buffer); 1654 } 1655 1656 int 1657 show_entry(void) 1658 { 1659 /* 1660 * Trim any remaining whitespace. 1661 */ 1662 if (outbuf.used != 0) { 1663 bool infodump = !TcOutput(); 1664 char delim = (char) (infodump ? ',' : ':'); 1665 int j; 1666 1667 for (j = (int) outbuf.used - 1; j > 0; --j) { 1668 char ch = outbuf.text[j]; 1669 if (ch == '\n') { 1670 ; 1671 } else if (isspace(UChar(ch))) { 1672 outbuf.used = (size_t) j; 1673 } else if (!infodump && ch == '\\') { 1674 outbuf.used = (size_t) j; 1675 } else if (ch == delim && (j == 0 || outbuf.text[j - 1] != '\\')) { 1676 outbuf.used = (size_t) (j + 1); 1677 } else { 1678 break; 1679 } 1680 } 1681 outbuf.text[outbuf.used] = '\0'; 1682 } 1683 if (outbuf.text != 0) { 1684 (void) fputs(outbuf.text, stdout); 1685 putchar('\n'); 1686 } 1687 return (int) outbuf.used; 1688 } 1689 1690 void 1691 compare_entry(PredHook hook, 1692 TERMTYPE2 *tp GCC_UNUSED, 1693 bool quiet) 1694 /* compare two entries */ 1695 { 1696 PredIdx i, j; 1697 NCURSES_CONST char *name; 1698 1699 if (!quiet) 1700 fputs(" comparing booleans.\n", stdout); 1701 for_each_boolean(j, tp) { 1702 i = BoolIndirect(j); 1703 name = ExtBoolname(tp, (int) i, bool_names); 1704 1705 if (isObsolete(outform, name)) 1706 continue; 1707 1708 (*hook) (CMP_BOOLEAN, i, name); 1709 } 1710 1711 if (!quiet) 1712 fputs(" comparing numbers.\n", stdout); 1713 for_each_number(j, tp) { 1714 i = NumIndirect(j); 1715 name = ExtNumname(tp, (int) i, num_names); 1716 1717 if (isObsolete(outform, name)) 1718 continue; 1719 1720 (*hook) (CMP_NUMBER, i, name); 1721 } 1722 1723 if (!quiet) 1724 fputs(" comparing strings.\n", stdout); 1725 for_each_string(j, tp) { 1726 i = StrIndirect(j); 1727 name = ExtStrname(tp, (int) i, str_names); 1728 1729 if (isObsolete(outform, name)) 1730 continue; 1731 1732 (*hook) (CMP_STRING, i, name); 1733 } 1734 1735 /* (void) fputs(" comparing use entries.\n", stdout); */ 1736 (*hook) (CMP_USE, 0, "use"); 1737 1738 } 1739 1740 #define NOTSET(s) ((s) == 0) 1741 1742 /* 1743 * This bit of legerdemain turns all the terminfo variable names into 1744 * references to locations in the arrays Booleans, Numbers, and Strings --- 1745 * precisely what's needed. 1746 */ 1747 #undef CUR 1748 #define CUR tp-> 1749 1750 static void 1751 set_obsolete_termcaps(TERMTYPE2 *tp) 1752 { 1753 #include "capdefaults.c" 1754 } 1755 1756 /* 1757 * Convert an alternate-character-set string to canonical form: sorted and 1758 * unique. 1759 */ 1760 void 1761 repair_acsc(TERMTYPE2 *tp) 1762 { 1763 if (VALID_STRING(acs_chars)) { 1764 size_t n, m; 1765 char mapped[256]; 1766 char extra = 0; 1767 unsigned source; 1768 unsigned target; 1769 bool fix_needed = FALSE; 1770 1771 for (n = 0, source = 0; acs_chars[n] != 0; n++) { 1772 target = UChar(acs_chars[n]); 1773 if (source >= target) { 1774 fix_needed = TRUE; 1775 break; 1776 } 1777 source = target; 1778 if (acs_chars[n + 1]) 1779 n++; 1780 } 1781 if (fix_needed) { 1782 memset(mapped, 0, sizeof(mapped)); 1783 for (n = 0; acs_chars[n] != 0; n++) { 1784 source = UChar(acs_chars[n]); 1785 if ((target = (unsigned char) acs_chars[n + 1]) != 0) { 1786 mapped[source] = (char) target; 1787 n++; 1788 } else { 1789 extra = (char) source; 1790 } 1791 } 1792 for (n = m = 0; n < sizeof(mapped); n++) { 1793 if (mapped[n]) { 1794 acs_chars[m++] = (char) n; 1795 acs_chars[m++] = mapped[n]; 1796 } 1797 } 1798 if (extra) 1799 acs_chars[m++] = extra; /* garbage in, garbage out */ 1800 acs_chars[m] = 0; 1801 } 1802 } 1803 } 1804