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