1 /* $OpenBSD: dump_entry.c,v 1.18 2003/04/06 21:12:07 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998-2000 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 ****************************************************************************/ 35 36 #define __INTERNAL_CAPS_VISIBLE 37 #include <progs.priv.h> 38 39 #include "dump_entry.h" 40 #include <termsort.c> /* this C file is generated */ 41 #include <parametrized.h> /* so is this */ 42 43 MODULE_ID("$From: dump_entry.c,v 1.54 2000/10/01 01:34:06 tom Exp $") 44 45 #define INDENT 8 46 #define DISCARD(string) string = ABSENT_STRING 47 #define PRINTF (void) printf 48 49 typedef struct { 50 char *text; 51 size_t used; 52 size_t size; 53 } DYNBUF; 54 55 static int tversion; /* terminfo version */ 56 static int outform; /* output format to use */ 57 static int sortmode; /* sort mode to use */ 58 static int width = 60; /* max line width for listings */ 59 static int column; /* current column, limited by 'width' */ 60 static int oldcol; /* last value of column before wrap */ 61 static int tracelevel; /* level of debug output */ 62 static bool pretty; /* true if we format if-then-else strings */ 63 64 static DYNBUF outbuf; 65 static DYNBUF tmpbuf; 66 67 /* indirection pointers for implementing sort and display modes */ 68 static const int *bool_indirect, *num_indirect, *str_indirect; 69 static NCURSES_CONST char *const *bool_names; 70 static NCURSES_CONST char *const *num_names; 71 static NCURSES_CONST char *const *str_names; 72 73 static const char *separator, *trailer; 74 75 /* cover various ports and variants of terminfo */ 76 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */ 77 #define V_SVR1 1 /* SVR1, Ultrix */ 78 #define V_HPUX 2 /* HP/UX */ 79 #define V_AIX 3 /* AIX */ 80 #define V_BSD 4 /* BSD */ 81 82 #if NCURSES_XNAMES 83 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T')) 84 #else 85 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T') 86 #endif 87 88 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n)) 89 90 #if NCURSES_XNAMES 91 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j])) 92 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j])) 93 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j])) 94 #else 95 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j]) 96 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j]) 97 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j]) 98 #endif 99 100 static void 101 strncpy_DYN(DYNBUF * dst, const char *src, size_t need) 102 { 103 size_t want = need + dst->used + 1; 104 if (want > dst->size) { 105 dst->size += (want + 1024); /* be generous */ 106 dst->text = typeRealloc(char, dst->size, dst->text); 107 } 108 (void) strncpy(dst->text + dst->used, src, need); 109 dst->used += need; 110 dst->text[dst->used] = 0; 111 } 112 113 static void 114 strcpy_DYN(DYNBUF * dst, const char *src) 115 { 116 if (src == 0) { 117 dst->used = 0; 118 strcpy_DYN(dst, ""); 119 } else { 120 strncpy_DYN(dst, src, strlen(src)); 121 } 122 } 123 124 #if NO_LEAKS 125 static void 126 free_DYN(DYNBUF * p) 127 { 128 if (p->text != 0) 129 free(p->text); 130 p->text = 0; 131 p->size = 0; 132 p->used = 0; 133 } 134 135 void 136 _nc_leaks_dump_entry(void) 137 { 138 free_DYN(&outbuf); 139 free_DYN(&tmpbuf); 140 } 141 #endif 142 143 NCURSES_CONST char * 144 nametrans(const char *name) 145 /* translate a capability name from termcap to terminfo */ 146 { 147 const struct name_table_entry *np; 148 149 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) 150 switch (np->nte_type) { 151 case BOOLEAN: 152 if (bool_from_termcap[np->nte_index]) 153 return (boolcodes[np->nte_index]); 154 break; 155 156 case NUMBER: 157 if (num_from_termcap[np->nte_index]) 158 return (numcodes[np->nte_index]); 159 break; 160 161 case STRING: 162 if (str_from_termcap[np->nte_index]) 163 return (strcodes[np->nte_index]); 164 break; 165 } 166 167 return (0); 168 } 169 170 void 171 dump_init(const char *version, int mode, int sort, int twidth, int traceval, 172 bool formatted) 173 /* set up for entry display */ 174 { 175 width = twidth; 176 pretty = formatted; 177 tracelevel = traceval; 178 179 /* versions */ 180 if (version == 0) 181 tversion = V_ALLCAPS; 182 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1") 183 || !strcmp(version, "Ultrix")) 184 tversion = V_SVR1; 185 else if (!strcmp(version, "HP")) 186 tversion = V_HPUX; 187 else if (!strcmp(version, "AIX")) 188 tversion = V_AIX; 189 else if (!strcmp(version, "BSD")) 190 tversion = V_BSD; 191 else 192 tversion = V_ALLCAPS; 193 194 /* implement display modes */ 195 switch (outform = mode) { 196 case F_LITERAL: 197 case F_TERMINFO: 198 bool_names = boolnames; 199 num_names = numnames; 200 str_names = strnames; 201 separator = twidth ? ", " : ","; 202 trailer = "\n\t"; 203 break; 204 205 case F_VARIABLE: 206 bool_names = boolfnames; 207 num_names = numfnames; 208 str_names = strfnames; 209 separator = twidth ? ", " : ","; 210 trailer = "\n\t"; 211 break; 212 213 case F_TERMCAP: 214 case F_TCONVERR: 215 bool_names = boolcodes; 216 num_names = numcodes; 217 str_names = strcodes; 218 separator = ":"; 219 trailer = "\\\n\t:"; 220 break; 221 } 222 223 /* implement sort modes */ 224 switch (sortmode = sort) { 225 case S_NOSORT: 226 if (traceval) 227 (void) fprintf(stderr, 228 "%s: sorting by term structure order\n", _nc_progname); 229 break; 230 231 case S_TERMINFO: 232 if (traceval) 233 (void) fprintf(stderr, 234 "%s: sorting by terminfo name order\n", _nc_progname); 235 bool_indirect = bool_terminfo_sort; 236 num_indirect = num_terminfo_sort; 237 str_indirect = str_terminfo_sort; 238 break; 239 240 case S_VARIABLE: 241 if (traceval) 242 (void) fprintf(stderr, 243 "%s: sorting by C variable order\n", _nc_progname); 244 bool_indirect = bool_variable_sort; 245 num_indirect = num_variable_sort; 246 str_indirect = str_variable_sort; 247 break; 248 249 case S_TERMCAP: 250 if (traceval) 251 (void) fprintf(stderr, 252 "%s: sorting by termcap name order\n", _nc_progname); 253 bool_indirect = bool_termcap_sort; 254 num_indirect = num_termcap_sort; 255 str_indirect = str_termcap_sort; 256 break; 257 } 258 259 if (traceval) 260 (void) fprintf(stderr, 261 "%s: width = %d, tversion = %d, outform = %d\n", 262 _nc_progname, width, tversion, outform); 263 } 264 265 static TERMTYPE *cur_type; 266 267 static int 268 dump_predicate(int type, int idx) 269 /* predicate function to use for ordinary decompilation */ 270 { 271 switch (type) { 272 case BOOLEAN: 273 return (cur_type->Booleans[idx] == FALSE) 274 ? FAIL : cur_type->Booleans[idx]; 275 276 case NUMBER: 277 return (cur_type->Numbers[idx] == ABSENT_NUMERIC) 278 ? FAIL : cur_type->Numbers[idx]; 279 280 case STRING: 281 return (cur_type->Strings[idx] != ABSENT_STRING) 282 ? (int) TRUE : FAIL; 283 } 284 285 return (FALSE); /* pacify compiler */ 286 } 287 288 static void set_obsolete_termcaps(TERMTYPE * tp); 289 290 /* is this the index of a function key string? */ 291 #define FNKEY(i) (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268)) 292 293 static bool 294 version_filter(int type, int idx) 295 /* filter out capabilities we may want to suppress */ 296 { 297 switch (tversion) { 298 case V_ALLCAPS: /* SVr4, XSI Curses */ 299 return (TRUE); 300 301 case V_SVR1: /* System V Release 1, Ultrix */ 302 switch (type) { 303 case BOOLEAN: 304 /* below and including xon_xoff */ 305 return ((idx <= 20) ? TRUE : FALSE); 306 case NUMBER: 307 /* below and including width_status_line */ 308 return ((idx <= 7) ? TRUE : FALSE); 309 case STRING: 310 /* below and including prtr_non */ 311 return ((idx <= 144) ? TRUE : FALSE); 312 } 313 break; 314 315 case V_HPUX: /* Hewlett-Packard */ 316 switch (type) { 317 case BOOLEAN: 318 /* below and including xon_xoff */ 319 return ((idx <= 20) ? TRUE : FALSE); 320 case NUMBER: 321 /* below and including label_width */ 322 return ((idx <= 10) ? TRUE : FALSE); 323 case STRING: 324 if (idx <= 144) /* below and including prtr_non */ 325 return (TRUE); 326 else if (FNKEY(idx)) /* function keys */ 327 return (TRUE); 328 else if (idx == 147 || idx == 156 || idx == 157) /* plab_norm,label_on,label_off */ 329 return (TRUE); 330 else 331 return (FALSE); 332 } 333 break; 334 335 case V_AIX: /* AIX */ 336 switch (type) { 337 case BOOLEAN: 338 /* below and including xon_xoff */ 339 return ((idx <= 20) ? TRUE : FALSE); 340 case NUMBER: 341 /* below and including width_status_line */ 342 return ((idx <= 7) ? TRUE : FALSE); 343 case STRING: 344 if (idx <= 144) /* below and including prtr_non */ 345 return (TRUE); 346 else if (FNKEY(idx)) /* function keys */ 347 return (TRUE); 348 else 349 return (FALSE); 350 } 351 break; 352 353 case V_BSD: /* BSD */ 354 switch (type) { 355 case BOOLEAN: 356 return bool_from_termcap[idx]; 357 case NUMBER: 358 return num_from_termcap[idx]; 359 case STRING: 360 return str_from_termcap[idx]; 361 } 362 break; 363 } 364 365 return (FALSE); /* pacify the compiler */ 366 } 367 368 static void 369 force_wrap(void) 370 { 371 oldcol = column; 372 strcpy_DYN(&outbuf, trailer); 373 column = INDENT; 374 } 375 376 static void 377 wrap_concat(const char *src) 378 { 379 int need = strlen(src); 380 int want = strlen(separator) + need; 381 382 if (column > INDENT 383 && column + want > width) { 384 force_wrap(); 385 } 386 strcpy_DYN(&outbuf, src); 387 strcpy_DYN(&outbuf, separator); 388 column += need; 389 } 390 391 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \ 392 if ((size_t)(last - first) > sizeof(sep_trail)-1 \ 393 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \ 394 first += sizeof(sep_trail)-2 395 396 /* Returns the nominal length of the buffer assuming it is termcap format, 397 * i.e., the continuation sequence is treated as a single character ":". 398 * 399 * There are several implementations of termcap which read the text into a 400 * fixed-size buffer. Generally they strip the newlines from the text, but may 401 * not do it until after the buffer is read. Also, "tc=" resolution may be 402 * expanded in the same buffer. This function is useful for measuring the size 403 * of the best fixed-buffer implementation; the worst case may be much worse. 404 */ 405 #ifdef TEST_TERMCAP_LENGTH 406 static int 407 termcap_length(const char *src) 408 { 409 static const char pattern[] = ":\\\n\t:"; 410 411 int len = 0; 412 const char *const t = src + strlen(src); 413 414 while (*src != '\0') { 415 IGNORE_SEP_TRAIL(src, t, pattern); 416 src++; 417 len++; 418 } 419 return len; 420 } 421 #else 422 #define termcap_length(src) strlen(src) 423 #endif 424 425 static char * 426 fmt_complex(char *src, int level) 427 { 428 int percent = 0; 429 int n; 430 bool if_then = strstr(src, "%?") != 0; 431 bool params = !if_then && (strlen(src) > 50) && (strstr(src, "%p") != 0); 432 433 while (*src != '\0') { 434 switch (*src) { 435 case '\\': 436 percent = 0; 437 strncpy_DYN(&tmpbuf, src++, 1); 438 break; 439 case '%': 440 percent = 1; 441 break; 442 case '?': /* "if" */ 443 case 't': /* "then" */ 444 case 'e': /* "else" */ 445 if (percent) { 446 percent = 0; 447 tmpbuf.text[tmpbuf.used - 1] = '\n'; 448 /* treat a "%e%?" as else-if, on the same level */ 449 if (!strncmp(src, "e%?", 3)) { 450 for (n = 0; n < level; n++) 451 strncpy_DYN(&tmpbuf, "\t", 1); 452 strncpy_DYN(&tmpbuf, "%", 1); 453 strncpy_DYN(&tmpbuf, src, 3); 454 src += 3; 455 } else { 456 for (n = 0; n <= level; n++) 457 strncpy_DYN(&tmpbuf, "\t", 1); 458 strncpy_DYN(&tmpbuf, "%", 1); 459 strncpy_DYN(&tmpbuf, src, 1); 460 if (*src++ == '?') { 461 src = fmt_complex(src, level + 1); 462 } else if (level == 1) { 463 _nc_warning("%%%c without %%?", *src); 464 } 465 } 466 continue; 467 } 468 break; 469 case ';': /* "endif" */ 470 if (percent) { 471 percent = 0; 472 if (level > 1) { 473 tmpbuf.text[tmpbuf.used - 1] = '\n'; 474 for (n = 0; n < level; n++) 475 strncpy_DYN(&tmpbuf, "\t", 1); 476 strncpy_DYN(&tmpbuf, "%", 1); 477 strncpy_DYN(&tmpbuf, src++, 1); 478 return src; 479 } 480 _nc_warning("%%; without %%?"); 481 } 482 break; 483 case 'p': 484 if (percent && params) { 485 tmpbuf.text[tmpbuf.used - 1] = '\n'; 486 for (n = 0; n <= level; n++) 487 strncpy_DYN(&tmpbuf, "\t", 1); 488 strncpy_DYN(&tmpbuf, "%", 1); 489 } 490 percent = 0; 491 break; 492 default: 493 percent = 0; 494 break; 495 } 496 strncpy_DYN(&tmpbuf, src++, 1); 497 } 498 return src; 499 } 500 501 int 502 fmt_entry(TERMTYPE * tterm, 503 int (*pred) (int type, int idx), 504 bool suppress_untranslatable, 505 bool infodump, 506 int numbers) 507 { 508 int i, j; 509 char buffer[MAX_TERMINFO_LENGTH]; 510 NCURSES_CONST char *name; 511 int predval, len; 512 int num_bools = 0; 513 int num_values = 0; 514 int num_strings = 0; 515 bool outcount = 0; 516 517 #define WRAP_CONCAT \ 518 wrap_concat(buffer); \ 519 outcount = TRUE 520 521 len = 12; /* terminfo file-header */ 522 523 if (pred == 0) { 524 cur_type = tterm; 525 pred = dump_predicate; 526 } 527 528 strcpy_DYN(&outbuf, 0); 529 strcpy_DYN(&outbuf, tterm->term_names); 530 strcpy_DYN(&outbuf, separator); 531 column = outbuf.used; 532 force_wrap(); 533 534 for_each_boolean(j, tterm) { 535 i = BoolIndirect(j); 536 name = ExtBoolname(tterm, i, bool_names); 537 538 if (!version_filter(BOOLEAN, i)) 539 continue; 540 else if (isObsolete(outform, name)) 541 continue; 542 543 predval = pred(BOOLEAN, i); 544 if (predval != FAIL) { 545 (void) strlcpy(buffer, name, sizeof buffer); 546 if (predval <= 0) 547 (void) strlcat(buffer, "@", sizeof buffer); 548 else if (i + 1 > num_bools) 549 num_bools = i + 1; 550 WRAP_CONCAT; 551 } 552 } 553 554 if (column != INDENT) 555 force_wrap(); 556 557 for_each_number(j, tterm) { 558 i = NumIndirect(j); 559 name = ExtNumname(tterm, i, num_names); 560 561 if (!version_filter(NUMBER, i)) 562 continue; 563 else if (isObsolete(outform, name)) 564 continue; 565 566 predval = pred(NUMBER, i); 567 if (predval != FAIL) { 568 if (tterm->Numbers[i] < 0) { 569 snprintf(buffer, sizeof buffer, "%s@", name); 570 } else { 571 snprintf(buffer, sizeof buffer, "%s#%d", name, tterm->Numbers[i]); 572 if (i + 1 > num_values) 573 num_values = i + 1; 574 } 575 WRAP_CONCAT; 576 } 577 } 578 579 if (column != INDENT) 580 force_wrap(); 581 582 len += num_bools 583 + num_values * 2 584 + strlen(tterm->term_names) + 1; 585 if (len & 1) 586 len++; 587 588 #undef CUR 589 #define CUR tterm-> 590 if (outform == F_TERMCAP) { 591 if (termcap_reset != ABSENT_STRING) { 592 if (init_3string != ABSENT_STRING 593 && !strcmp(init_3string, termcap_reset)) 594 DISCARD(init_3string); 595 596 if (reset_2string != ABSENT_STRING 597 && !strcmp(reset_2string, termcap_reset)) 598 DISCARD(reset_2string); 599 } 600 } 601 602 for_each_string(j, tterm) { 603 i = StrIndirect(j); 604 name = ExtStrname(tterm, i, str_names); 605 606 if (!version_filter(STRING, i)) 607 continue; 608 else if (isObsolete(outform, name)) 609 continue; 610 611 /* 612 * Some older versions of vi want rmir/smir to be defined 613 * for ich/ich1 to work. If they're not defined, force 614 * them to be output as defined and empty. 615 */ 616 if (outform == F_TERMCAP) { 617 if (insert_character || parm_ich) { 618 if (&tterm->Strings[i] == &enter_insert_mode 619 && enter_insert_mode == ABSENT_STRING) { 620 (void) strlcpy(buffer, "im=", sizeof buffer); 621 WRAP_CONCAT; 622 continue; 623 } 624 625 if (&tterm->Strings[i] == &exit_insert_mode 626 && exit_insert_mode == ABSENT_STRING) { 627 (void) strlcpy(buffer, "ei=", sizeof buffer); 628 WRAP_CONCAT; 629 continue; 630 } 631 } 632 } 633 634 predval = pred(STRING, i); 635 buffer[0] = '\0'; 636 637 if (predval != FAIL) { 638 if (tterm->Strings[i] != ABSENT_STRING 639 && i + 1 > num_strings) 640 num_strings = i + 1; 641 642 if (!VALID_STRING(tterm->Strings[i])) { 643 snprintf(buffer, sizeof buffer, "%s@", name); 644 WRAP_CONCAT; 645 } else if (outform == F_TERMCAP || outform == F_TCONVERR) { 646 int params = (i < (int) SIZEOF(parametrized)) ? parametrized[i] : 0; 647 char *srccap = _nc_tic_expand(tterm->Strings[i], TRUE, numbers); 648 char *cv = _nc_infotocap(name, srccap, params); 649 650 if (cv == 0) { 651 if (outform == F_TCONVERR) { 652 snprintf(buffer, sizeof buffer, 653 "%s=!!! %s WILL NOT CONVERT !!!", 654 name, srccap); 655 } else if (suppress_untranslatable) { 656 continue; 657 } else { 658 char *d, *s = srccap; 659 snprintf(buffer, sizeof buffer, "..%s=", name); 660 d = buffer + strlen(buffer); 661 while ((*d = *s++) != 0) { /* XXX overflow? */ 662 if (*d == ':') { 663 *d++ = '\\'; 664 *d = ':'; 665 } else if (*d == '\\') { 666 *++d = *s++; 667 } 668 d++; 669 } 670 } 671 } else { 672 snprintf(buffer, sizeof buffer, "%s=%s", name, cv); 673 } 674 len += strlen(tterm->Strings[i]) + 1; 675 WRAP_CONCAT; 676 } else { 677 char *src = _nc_tic_expand(tterm->Strings[i], 678 outform == F_TERMINFO, numbers); 679 680 strcpy_DYN(&tmpbuf, 0); 681 strcpy_DYN(&tmpbuf, name); 682 strcpy_DYN(&tmpbuf, "="); 683 if (pretty 684 && (outform == F_TERMINFO 685 || outform == F_VARIABLE)) { 686 fmt_complex(src, 1); 687 } else { 688 strcpy_DYN(&tmpbuf, src); 689 } 690 len += strlen(tterm->Strings[i]) + 1; 691 wrap_concat(tmpbuf.text); 692 outcount = TRUE; 693 } 694 } 695 } 696 len += num_strings * 2; 697 698 /* 699 * This piece of code should be an effective inverse of the functions 700 * postprocess_terminfo and postprocess_terminfo in parse_entry.c. 701 * Much more work should be done on this to support dumping termcaps. 702 */ 703 if (tversion == V_HPUX) { 704 if (memory_lock) { 705 (void) snprintf(buffer, sizeof buffer, "meml=%s", memory_lock); 706 WRAP_CONCAT; 707 } 708 if (memory_unlock) { 709 (void) snprintf(buffer, sizeof buffer, "memu=%s", memory_unlock); 710 WRAP_CONCAT; 711 } 712 } else if (tversion == V_AIX) { 713 if (VALID_STRING(acs_chars)) { 714 bool box_ok = TRUE; 715 const char *acstrans = "lqkxjmwuvtn"; 716 const char *cp; 717 char *tp, *sp, boxchars[11]; 718 719 tp = boxchars; 720 for (cp = acstrans; *cp; cp++) { 721 sp = strchr(acs_chars, *cp); 722 if (sp) 723 *tp++ = sp[1]; 724 else { 725 box_ok = FALSE; 726 break; 727 } 728 } 729 tp[0] = '\0'; 730 731 if (box_ok) { 732 (void) strlcpy(buffer, "box1=", sizeof buffer); 733 (void) strlcat(buffer, _nc_tic_expand(boxchars, 734 outform == F_TERMINFO, numbers), sizeof buffer); 735 WRAP_CONCAT; 736 } 737 } 738 } 739 740 /* 741 * kludge: trim off trailer to avoid an extra blank line 742 * in infocmp -u output when there are no string differences 743 */ 744 if (outcount) { 745 bool trimmed = FALSE; 746 j = outbuf.used; 747 if (j >= 2 748 && outbuf.text[j - 1] == '\t' 749 && outbuf.text[j - 2] == '\n') { 750 outbuf.used -= 2; 751 trimmed = TRUE; 752 } else if (j >= 4 753 && outbuf.text[j - 1] == ':' 754 && outbuf.text[j - 2] == '\t' 755 && outbuf.text[j - 3] == '\n' 756 && outbuf.text[j - 4] == '\\') { 757 outbuf.used -= 4; 758 trimmed = TRUE; 759 } 760 if (trimmed) { 761 outbuf.text[outbuf.used] = '\0'; 762 column = oldcol; 763 } 764 } 765 #if 0 766 fprintf(stderr, "num_bools = %d\n", num_bools); 767 fprintf(stderr, "num_values = %d\n", num_values); 768 fprintf(stderr, "num_strings = %d\n", num_strings); 769 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", 770 tterm->term_names, len, outbuf.used, outbuf.text); 771 #endif 772 /* 773 * Here's where we use infodump to trigger a more stringent length check 774 * for termcap-translation purposes. 775 * Return the length of the raw entry, without tc= expansions, 776 * It gives an idea of which entries are deadly to even *scan past*, 777 * as opposed to *use*. 778 */ 779 return (infodump ? len : termcap_length(outbuf.text)); 780 } 781 782 int 783 dump_entry(TERMTYPE * tterm, bool limited, int numbers, int (*pred) (int 784 type, int idx)) 785 /* dump a single entry */ 786 { 787 int len, critlen; 788 const char *legend; 789 bool infodump; 790 791 if (outform == F_TERMCAP || outform == F_TCONVERR) { 792 critlen = MAX_TERMCAP_LENGTH; 793 legend = "older termcap"; 794 infodump = FALSE; 795 set_obsolete_termcaps(tterm); 796 } else { 797 critlen = MAX_TERMINFO_LENGTH; 798 legend = "terminfo"; 799 infodump = TRUE; 800 } 801 802 if (((len = fmt_entry(tterm, pred, FALSE, infodump, numbers)) > critlen) 803 && limited) { 804 PRINTF("# (untranslatable capabilities removed to fit entry within %d bytes)\n", 805 critlen); 806 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 807 /* 808 * We pick on sgr because it's a nice long string capability that 809 * is really just an optimization hack. Another good candidate is 810 * acsc since it is both long and unused by BSD termcap. 811 */ 812 char *oldsgr = set_attributes; 813 char *oldacsc = acs_chars; 814 set_attributes = ABSENT_STRING; 815 PRINTF("# (sgr removed to fit entry within %d bytes)\n", 816 critlen); 817 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 818 acs_chars = ABSENT_STRING; 819 PRINTF("# (acsc removed to fit entry within %d bytes)\n", 820 critlen); 821 } 822 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) { 823 int oldversion = tversion; 824 825 tversion = V_BSD; 826 PRINTF("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", 827 critlen); 828 829 if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) 830 > critlen) { 831 (void) fprintf(stderr, 832 "warning: %s entry is %d bytes long\n", 833 _nc_first_name(tterm->term_names), 834 len); 835 PRINTF( 836 "# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", 837 len, legend); 838 } 839 tversion = oldversion; 840 } 841 set_attributes = oldsgr; 842 acs_chars = oldacsc; 843 } 844 } 845 846 (void) fputs(outbuf.text, stdout); 847 return len; 848 } 849 850 int 851 dump_uses(const char *name, bool infodump) 852 /* dump "use=" clauses in the appropriate format */ 853 { 854 char buffer[MAX_TERMINFO_LENGTH]; 855 856 strcpy_DYN(&outbuf, 0); 857 (void) snprintf(buffer, sizeof buffer, 858 "%s%s", infodump ? "use=" : "tc=", name); 859 wrap_concat(buffer); 860 (void) fputs(outbuf.text, stdout); 861 return outbuf.used; 862 } 863 864 void 865 compare_entry(void (*hook) (int t, int i, const char *name), TERMTYPE * tp 866 GCC_UNUSED, bool quiet) 867 /* compare two entries */ 868 { 869 int i, j; 870 NCURSES_CONST char *name; 871 872 if (!quiet) 873 fputs(" comparing booleans.\n", stdout); 874 for_each_boolean(j, tp) { 875 i = BoolIndirect(j); 876 name = ExtBoolname(tp, i, bool_names); 877 878 if (isObsolete(outform, name)) 879 continue; 880 881 (*hook) (CMP_BOOLEAN, i, name); 882 } 883 884 if (!quiet) 885 fputs(" comparing numbers.\n", stdout); 886 for_each_number(j, tp) { 887 i = NumIndirect(j); 888 name = ExtNumname(tp, i, num_names); 889 890 if (isObsolete(outform, name)) 891 continue; 892 893 (*hook) (CMP_NUMBER, i, name); 894 } 895 896 if (!quiet) 897 fputs(" comparing strings.\n", stdout); 898 for_each_string(j, tp) { 899 i = StrIndirect(j); 900 name = ExtStrname(tp, i, str_names); 901 902 if (isObsolete(outform, name)) 903 continue; 904 905 (*hook) (CMP_STRING, i, name); 906 } 907 908 /* (void) fputs(" comparing use entries.\n", stdout); */ 909 (*hook) (CMP_USE, 0, "use"); 910 911 } 912 913 #define NOTSET(s) ((s) == 0) 914 915 /* 916 * This bit of legerdemain turns all the terminfo variable names into 917 * references to locations in the arrays Booleans, Numbers, and Strings --- 918 * precisely what's needed. 919 */ 920 #undef CUR 921 #define CUR tp-> 922 923 static void 924 set_obsolete_termcaps(TERMTYPE * tp) 925 { 926 #include "capdefaults.c" 927 } 928 929 /* 930 * Convert an alternate-character-set string to canonical form: sorted and 931 * unique. 932 */ 933 void 934 repair_acsc(TERMTYPE * tp) 935 { 936 if (VALID_STRING(acs_chars)) { 937 size_t n, m; 938 char mapped[256]; 939 char extra = 0; 940 unsigned source; 941 unsigned target; 942 bool fix_needed = FALSE; 943 944 for (n = 0, source = 0; acs_chars[n] != 0; n++) { 945 target = acs_chars[n]; 946 if (source >= target) { 947 fix_needed = TRUE; 948 break; 949 } 950 source = target; 951 if (acs_chars[n + 1]) 952 n++; 953 } 954 if (fix_needed) { 955 memset(mapped, 0, sizeof(mapped)); 956 for (n = 0; acs_chars[n] != 0; n++) { 957 source = acs_chars[n]; 958 if ((target = (unsigned char) acs_chars[n + 1]) != 0) { 959 mapped[source] = target; 960 n++; 961 } else { 962 extra = source; 963 } 964 } 965 for (n = m = 0; n < sizeof(mapped); n++) { 966 if (mapped[n]) { 967 acs_chars[m++] = n; 968 acs_chars[m++] = mapped[n]; 969 } 970 } 971 if (extra) 972 acs_chars[m++] = extra; /* garbage in, garbage out */ 973 acs_chars[m] = 0; 974 } 975 } 976 } 977