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