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