1 /****************************************************************************
2 * Copyright (c) 1998-2015,2016 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 * and: Thomas E. Dickey 1996 on *
33 ****************************************************************************/
35 #define __INTERNAL_CAPS_VISIBLE
36 #include <progs.priv.h>
38 #include "dump_entry.h"
39 #include "termsort.c" /* this C file is generated */
40 #include <parametrized.h> /* so is this */
42 MODULE_ID("$Id: dump_entry.c,v 1.126 2016/09/10 20:41:23 tom Exp $")
44 #define DISCARD(string) string = ABSENT_STRING
45 #define PRINTF (void) printf
47 #define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array))
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 height = 65535; /* max number of lines for listings */
60 static int column; /* current column, limited by 'width' */
61 static int oldcol; /* last value of column before wrap */
62 static bool pretty; /* true if we format if-then-else strings */
63 static bool checking; /* true if we are checking for tic */
64 static int quickdump; /* true if we are dumping compiled data */
66 static char *save_sgr;
71 /* indirection pointers for implementing sort and display modes */
72 static const PredIdx *bool_indirect, *num_indirect, *str_indirect;
73 static NCURSES_CONST char *const *bool_names;
74 static NCURSES_CONST char *const *num_names;
75 static NCURSES_CONST char *const *str_names;
77 static const char *separator = "", *trailer = "";
78 static int indent = 8;
80 /* cover various ports and variants of terminfo */
81 #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */
82 #define V_SVR1 1 /* SVR1, Ultrix */
83 #define V_HPUX 2 /* HP/UX */
84 #define V_AIX 3 /* AIX */
85 #define V_BSD 4 /* BSD */
88 #define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T'))
90 #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T')
93 #define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n))
96 #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j]))
97 #define NumIndirect(j) ((j >= NUMCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : num_indirect[j]))
98 #define StrIndirect(j) ((j >= STRCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : str_indirect[j]))
100 #define BoolIndirect(j) ((sortmode == S_NOSORT) ? (j) : bool_indirect[j])
101 #define NumIndirect(j) ((sortmode == S_NOSORT) ? (j) : num_indirect[j])
102 #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j])
105 static void failed(const char *) GCC_NORETURN;
108 failed(const char *s)
111 ExitProgram(EXIT_FAILURE);
115 strncpy_DYN(DYNBUF * dst, const char *src, size_t need)
117 size_t want = need + dst->used + 1;
118 if (want > dst->size) {
119 dst->size += (want + 1024); /* be generous */
120 dst->text = typeRealloc(char, dst->size, dst->text);
122 failed("strncpy_DYN");
124 _nc_STRNCPY(dst->text + dst->used, src, need);
126 dst->text[dst->used] = 0;
130 strcpy_DYN(DYNBUF * dst, const char *src)
136 strncpy_DYN(dst, src, strlen(src));
152 _nc_leaks_dump_entry(void)
159 #define NameTrans(check,result) \
160 if ((np->nte_index <= OK_ ## check) \
161 && check[np->nte_index]) \
162 return (result[np->nte_index])
165 nametrans(const char *name)
166 /* translate a capability name to termcap from terminfo */
168 const struct name_table_entry *np;
170 if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) {
171 switch (np->nte_type) {
173 NameTrans(bool_from_termcap, boolcodes);
177 NameTrans(num_from_termcap, numcodes);
181 NameTrans(str_from_termcap, strcodes);
190 dump_init(const char *version,
199 /* set up for entry display */
205 quickdump = (quick & 3);
209 tversion = V_ALLCAPS;
210 else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1")
211 || !strcmp(version, "Ultrix"))
213 else if (!strcmp(version, "HP"))
215 else if (!strcmp(version, "AIX"))
217 else if (!strcmp(version, "BSD"))
220 tversion = V_ALLCAPS;
222 /* implement display modes */
223 switch (outform = mode) {
226 bool_names = boolnames;
227 num_names = numnames;
228 str_names = strnames;
229 separator = (twidth > 0 && theight > 1) ? ", " : ",";
234 bool_names = boolfnames;
235 num_names = numfnames;
236 str_names = strfnames;
237 separator = (twidth > 0 && theight > 1) ? ", " : ",";
243 bool_names = boolcodes;
244 num_names = numcodes;
245 str_names = strcodes;
252 /* implement sort modes */
253 switch (sortmode = sort) {
256 (void) fprintf(stderr,
257 "%s: sorting by term structure order\n", _nc_progname);
262 (void) fprintf(stderr,
263 "%s: sorting by terminfo name order\n", _nc_progname);
264 bool_indirect = bool_terminfo_sort;
265 num_indirect = num_terminfo_sort;
266 str_indirect = str_terminfo_sort;
271 (void) fprintf(stderr,
272 "%s: sorting by C variable order\n", _nc_progname);
273 bool_indirect = bool_variable_sort;
274 num_indirect = num_variable_sort;
275 str_indirect = str_variable_sort;
280 (void) fprintf(stderr,
281 "%s: sorting by termcap name order\n", _nc_progname);
282 bool_indirect = bool_termcap_sort;
283 num_indirect = num_termcap_sort;
284 str_indirect = str_termcap_sort;
289 (void) fprintf(stderr,
290 "%s: width = %d, tversion = %d, outform = %d\n",
291 _nc_progname, width, tversion, outform);
294 static TERMTYPE *cur_type;
297 dump_predicate(PredType type, PredIdx idx)
298 /* predicate function to use for ordinary decompilation */
302 return (cur_type->Booleans[idx] == FALSE)
303 ? FAIL : cur_type->Booleans[idx];
306 return (cur_type->Numbers[idx] == ABSENT_NUMERIC)
307 ? FAIL : cur_type->Numbers[idx];
310 return (cur_type->Strings[idx] != ABSENT_STRING)
314 return (FALSE); /* pacify compiler */
317 static void set_obsolete_termcaps(TERMTYPE *tp);
319 /* is this the index of a function key string? */
321 (((i) >= STR_IDX(key_f0) && \
322 (i) <= STR_IDX(key_f9)) || \
323 ((i) >= STR_IDX(key_f11) && \
324 (i) <= STR_IDX(key_f63)))
327 * If we configure with a different Caps file, the offsets into the arrays
328 * will change. So we use an address expression.
330 #define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0]))
331 #define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0]))
332 #define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0]))
335 version_filter(PredType type, PredIdx idx)
336 /* filter out capabilities we may want to suppress */
339 case V_ALLCAPS: /* SVr4, XSI Curses */
342 case V_SVR1: /* System V Release 1, Ultrix */
345 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
347 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
349 return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE);
353 case V_HPUX: /* Hewlett-Packard */
356 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
358 return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE);
360 if (idx <= STR_IDX(prtr_non))
362 else if (FNKEY(idx)) /* function keys */
364 else if (idx == STR_IDX(plab_norm)
365 || idx == STR_IDX(label_on)
366 || idx == STR_IDX(label_off))
373 case V_AIX: /* AIX */
376 return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE);
378 return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE);
380 if (idx <= STR_IDX(prtr_non))
382 else if (FNKEY(idx)) /* function keys */
389 #define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \
390 type##_from_termcap[idx])
392 case V_BSD: /* BSD */
395 return is_termcap(bool);
397 return is_termcap(num);
399 return is_termcap(str);
404 return (FALSE); /* pacify the compiler */
410 while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ')
411 outbuf.text[--outbuf.used] = '\0';
419 strcpy_DYN(&outbuf, trailer);
424 wrap_concat(const char *src)
426 size_t need = strlen(src);
427 size_t want = strlen(separator) + need;
430 && column + (int) want > width) {
433 strcpy_DYN(&outbuf, src);
434 strcpy_DYN(&outbuf, separator);
435 column += (int) need;
438 #define IGNORE_SEP_TRAIL(first,last,sep_trail) \
439 if ((size_t)(last - first) > sizeof(sep_trail)-1 \
440 && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \
441 first += sizeof(sep_trail)-2
443 /* Returns the nominal length of the buffer assuming it is termcap format,
444 * i.e., the continuation sequence is treated as a single character ":".
446 * There are several implementations of termcap which read the text into a
447 * fixed-size buffer. Generally they strip the newlines from the text, but may
448 * not do it until after the buffer is read. Also, "tc=" resolution may be
449 * expanded in the same buffer. This function is useful for measuring the size
450 * of the best fixed-buffer implementation; the worst case may be much worse.
452 #ifdef TEST_TERMCAP_LENGTH
454 termcap_length(const char *src)
456 static const char pattern[] = ":\\\n\t:";
459 const char *const t = src + strlen(src);
461 while (*src != '\0') {
462 IGNORE_SEP_TRAIL(src, t, pattern);
469 #define termcap_length(src) strlen(src)
473 indent_DYN(DYNBUF * buffer, int level)
477 for (n = 0; n < level; n++)
478 strncpy_DYN(buffer, "\t", (size_t) 1);
482 has_params(const char *src)
485 int len = (int) strlen(src);
490 for (n = 0; n < len - 1; ++n) {
491 if (!strncmp(src + n, "%p", (size_t) 2)) {
493 } else if (!strncmp(src + n, "%;", (size_t) 2)) {
500 result = ((len > 50) && params);
506 fmt_complex(TERMTYPE *tterm, const char *capability, char *src, int level)
508 bool percent = FALSE;
509 bool params = has_params(src);
511 while (*src != '\0') {
515 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
519 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
525 case 't': /* "then" */
526 case 'e': /* "else" */
529 tmpbuf.text[tmpbuf.used - 1] = '\n';
530 /* treat a "%e" as else-if, on the same level */
532 indent_DYN(&tmpbuf, level);
533 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
534 strncpy_DYN(&tmpbuf, src, (size_t) 1);
536 params = has_params(src);
537 if (!params && *src != '\0' && *src != '%') {
538 strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
539 indent_DYN(&tmpbuf, level + 1);
542 indent_DYN(&tmpbuf, level + 1);
543 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
544 strncpy_DYN(&tmpbuf, src, (size_t) 1);
546 src = fmt_complex(tterm, capability, src, level + 1);
547 if (*src != '\0' && *src != '%') {
548 strncpy_DYN(&tmpbuf, "\n", (size_t) 1);
549 indent_DYN(&tmpbuf, level + 1);
551 } else if (level == 1) {
553 _nc_warning("%s: %%%c without %%? in %s",
554 _nc_first_name(tterm->term_names),
561 case ';': /* "endif" */
565 tmpbuf.text[tmpbuf.used - 1] = '\n';
566 indent_DYN(&tmpbuf, level);
567 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
568 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
571 && (strchr("?e;", src[1])) == 0) {
572 tmpbuf.text[tmpbuf.used++] = '\n';
573 indent_DYN(&tmpbuf, level);
578 _nc_warning("%s: %%; without %%? in %s",
579 _nc_first_name(tterm->term_names),
584 if (percent && params) {
585 tmpbuf.text[tmpbuf.used - 1] = '\n';
586 indent_DYN(&tmpbuf, level + 1);
587 strncpy_DYN(&tmpbuf, "%", (size_t) 1);
593 strncpy_DYN(&tmpbuf, "\\s", (size_t) 2);
600 strncpy_DYN(&tmpbuf, src++, (size_t) 1);
605 #define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap)
609 fmt_entry(TERMTYPE *tterm,
612 int suppress_untranslatable,
617 char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP];
619 NCURSES_CONST char *name;
621 PredIdx num_bools = 0;
622 PredIdx num_values = 0;
623 PredIdx num_strings = 0;
626 #define WRAP_CONCAT \
627 wrap_concat(buffer); \
630 len = 12; /* terminfo file-header */
634 pred = dump_predicate;
637 strcpy_DYN(&outbuf, 0);
639 column = indent; /* FIXME: workaround to prevent empty lines */
641 strcpy_DYN(&outbuf, tterm->term_names);
644 * Colon is legal in terminfo descriptions, but not in termcap.
647 char *p = outbuf.text;
655 strcpy_DYN(&outbuf, separator);
656 column = (int) outbuf.used;
661 for_each_boolean(j, tterm) {
663 name = ExtBoolname(tterm, (int) i, bool_names);
664 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
666 if (!version_filter(BOOLEAN, i))
668 else if (isObsolete(outform, name))
671 predval = pred(BOOLEAN, i);
672 if (predval != FAIL) {
673 _nc_STRCPY(buffer, name, sizeof(buffer));
675 _nc_STRCAT(buffer, "@", sizeof(buffer));
676 else if (i + 1 > num_bools)
682 if (column != indent && height > 1)
685 for_each_number(j, tterm) {
687 name = ExtNumname(tterm, (int) i, num_names);
688 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
690 if (!version_filter(NUMBER, i))
692 else if (isObsolete(outform, name))
695 predval = pred(NUMBER, i);
696 if (predval != FAIL) {
697 if (tterm->Numbers[i] < 0) {
698 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
701 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
702 "%s#%d", name, tterm->Numbers[i]);
703 if (i + 1 > num_values)
710 if (column != indent && height > 1)
713 len += (int) (num_bools
715 + strlen(tterm->term_names) + 1);
721 if (outform == F_TERMCAP) {
722 if (termcap_reset != ABSENT_STRING) {
723 if (init_3string != ABSENT_STRING
724 && !strcmp(init_3string, termcap_reset))
725 DISCARD(init_3string);
727 if (reset_2string != ABSENT_STRING
728 && !strcmp(reset_2string, termcap_reset))
729 DISCARD(reset_2string);
733 for_each_string(j, tterm) {
735 name = ExtStrname(tterm, (int) i, str_names);
736 assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);
738 capability = tterm->Strings[i];
740 if (!version_filter(STRING, i))
742 else if (isObsolete(outform, name))
747 * Extended names can be longer than 2 characters, but termcap programs
748 * cannot read those (filter them out).
750 if (outform == F_TERMCAP && (strlen(name) > 2))
754 if (outform == F_TERMCAP) {
756 * Some older versions of vi want rmir/smir to be defined
757 * for ich/ich1 to work. If they're not defined, force
758 * them to be output as defined and empty.
760 if (PRESENT(insert_character) || PRESENT(parm_ich)) {
761 if (SAME_CAP(i, enter_insert_mode)
762 && enter_insert_mode == ABSENT_STRING) {
763 _nc_STRCPY(buffer, "im=", sizeof(buffer));
768 if (SAME_CAP(i, exit_insert_mode)
769 && exit_insert_mode == ABSENT_STRING) {
770 _nc_STRCPY(buffer, "ei=", sizeof(buffer));
776 * termcap applications such as screen will be confused if sgr0
777 * is translated to a string containing rmacs. Filter that out.
779 if (PRESENT(exit_attribute_mode)) {
780 if (SAME_CAP(i, exit_attribute_mode)) {
782 char *my_sgr = set_attributes;
784 set_attributes = save_sgr;
786 trimmed_sgr0 = _nc_trim_sgr0(tterm);
787 if (strcmp(capability, trimmed_sgr0))
788 capability = trimmed_sgr0;
790 if (trimmed_sgr0 != exit_attribute_mode)
794 set_attributes = my_sgr;
799 predval = pred(STRING, i);
802 if (predval != FAIL) {
803 if (capability != ABSENT_STRING
804 && i + 1 > num_strings)
807 if (!VALID_STRING(capability)) {
808 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
811 } else if (outform == F_TERMCAP || outform == F_TCONVERR) {
812 char *srccap = _nc_tic_expand(capability, TRUE, numbers);
813 int params = (((i < (int) SIZEOF(parametrized)) &&
818 : has_params(srccap)));
819 char *cv = _nc_infotocap(name, srccap, params);
822 if (outform == F_TCONVERR) {
823 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
824 "%s=!!! %s WILL NOT CONVERT !!!",
826 } else if (suppress_untranslatable) {
829 char *s = srccap, *d = buffer;
830 _nc_SPRINTF(d, _nc_SLIMIT(sizeof(buffer)) "..%s=", name);
832 while ((*d = *s++) != 0) {
836 } else if (*d == '\\') {
843 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
846 len += (int) strlen(capability) + 1;
849 char *src = _nc_tic_expand(capability,
850 outform == F_TERMINFO, numbers);
852 strcpy_DYN(&tmpbuf, 0);
853 strcpy_DYN(&tmpbuf, name);
854 strcpy_DYN(&tmpbuf, "=");
856 && (outform == F_TERMINFO
857 || outform == F_VARIABLE)) {
858 fmt_complex(tterm, name, src, 1);
860 strcpy_DYN(&tmpbuf, src);
862 len += (int) strlen(capability) + 1;
863 wrap_concat(tmpbuf.text);
867 /* e.g., trimmed_sgr0 */
868 if (capability != ABSENT_STRING &&
869 capability != CANCELLED_STRING &&
870 capability != tterm->Strings[i])
873 len += (int) (num_strings * 2);
876 * This piece of code should be an effective inverse of the functions
877 * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c.
878 * Much more work should be done on this to support dumping termcaps.
880 if (tversion == V_HPUX) {
881 if (VALID_STRING(memory_lock)) {
882 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
883 "meml=%s", memory_lock);
886 if (VALID_STRING(memory_unlock)) {
887 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
888 "memu=%s", memory_unlock);
891 } else if (tversion == V_AIX) {
892 if (VALID_STRING(acs_chars)) {
894 const char *acstrans = "lqkxjmwuvtn";
896 char *tp, *sp, boxchars[11];
899 for (cp = acstrans; *cp; cp++) {
900 sp = (strchr) (acs_chars, *cp);
911 char *tmp = _nc_tic_expand(boxchars,
912 (outform == F_TERMINFO),
914 _nc_STRCPY(buffer, "box1=", sizeof(buffer));
915 while (*tmp != '\0') {
916 size_t have = strlen(buffer);
917 size_t next = strlen(tmp);
918 size_t want = have + next + 1;
923 * If the expanded string is too long for the buffer,
924 * chop it off and save the location where we chopped it.
926 if (want >= sizeof(buffer)) {
930 _nc_STRCAT(buffer, tmp, sizeof(buffer));
933 * If we chopped the buffer, replace the missing piece and
934 * shift everything to append the remainder.
939 while ((tmp[next] = tmp[last + next]) != '\0') {
952 * kludge: trim off trailer to avoid an extra blank line
953 * in infocmp -u output when there are no string differences
956 bool trimmed = FALSE;
957 j = (PredIdx) outbuf.used;
959 && outbuf.text[j - 1] == '\t'
960 && outbuf.text[j - 2] == '\n') {
964 && outbuf.text[j - 1] == ':'
965 && outbuf.text[j - 2] == '\t'
966 && outbuf.text[j - 3] == '\n'
967 && outbuf.text[j - 4] == '\\') {
972 outbuf.text[outbuf.used] = '\0';
974 strcpy_DYN(&outbuf, " ");
978 fprintf(stderr, "num_bools = %d\n", num_bools);
979 fprintf(stderr, "num_values = %d\n", num_values);
980 fprintf(stderr, "num_strings = %d\n", num_strings);
981 fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
982 tterm->term_names, len, outbuf.used, outbuf.text);
985 * Here's where we use infodump to trigger a more stringent length check
986 * for termcap-translation purposes.
987 * Return the length of the raw entry, without tc= expansions,
988 * It gives an idea of which entries are deadly to even *scan past*,
989 * as opposed to *use*.
991 return (infodump ? len : (int) termcap_length(outbuf.text));
995 kill_string(TERMTYPE *tterm, char *cap)
998 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
999 if (cap == tterm->Strings[n]) {
1000 tterm->Strings[n] = ABSENT_STRING;
1008 find_string(TERMTYPE *tterm, char *name)
1011 for (n = 0; n < NUM_STRINGS(tterm); ++n) {
1012 if (version_filter(STRING, n)
1013 && !strcmp(name, strnames[n])) {
1014 char *cap = tterm->Strings[n];
1015 if (VALID_STRING(cap)) {
1021 return ABSENT_STRING;
1025 * This is used to remove function-key labels from a termcap entry to
1029 kill_labels(TERMTYPE *tterm, int target)
1036 for (n = 0; n <= 10; ++n) {
1037 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n);
1038 if ((cap = find_string(tterm, name)) != ABSENT_STRING
1039 && kill_string(tterm, cap)) {
1040 target -= (int) (strlen(cap) + 5);
1050 * This is used to remove function-key definitions from a termcap entry to
1054 kill_fkeys(TERMTYPE *tterm, int target)
1061 for (n = 60; n >= 0; --n) {
1062 _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n);
1063 if ((cap = find_string(tterm, name)) != ABSENT_STRING
1064 && kill_string(tterm, cap)) {
1065 target -= (int) (strlen(cap) + 5);
1075 * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100.
1076 * Also, since this is for termcap, we only care about the line-drawing map.
1078 #define isLine(c) (strchr("lmkjtuvwqxn", c) != 0)
1081 one_one_mapping(const char *mapping)
1085 if (mapping != ABSENT_STRING) {
1087 while (mapping[n] != '\0') {
1088 if (isLine(mapping[n]) &&
1089 mapping[n] != mapping[n + 1]) {
1099 #define FMT_ENTRY() \
1100 fmt_entry(tterm, pred, \
1102 suppress_untranslatable, \
1105 #define SHOW_WHY PRINTF
1108 purged_acs(TERMTYPE *tterm)
1110 bool result = FALSE;
1112 if (VALID_STRING(acs_chars)) {
1113 if (!one_one_mapping(acs_chars)) {
1114 enter_alt_charset_mode = ABSENT_STRING;
1115 exit_alt_charset_mode = ABSENT_STRING;
1116 SHOW_WHY("# (rmacs/smacs removed for consistency)\n");
1124 encode_b64(char *target, char *source, unsigned state, int *saved)
1127 static const char data[] =
1128 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1129 "abcdefghijklmnopqrstuvwxyz"
1131 int ch = UChar(source[state]);
1133 switch (state % 3) {
1135 *target++ = data[ch & 077];
1136 *saved = (ch >> 6) & 3;
1139 *target++ = data[((ch << 2) | *saved) & 077];
1140 *saved = (ch >> 4) & 017;
1143 *target++ = data[((ch << 4) | *saved) & 077];
1144 *target++ = data[(ch >> 2) & 077];
1152 * Dump a single entry.
1155 dump_entry(TERMTYPE *tterm,
1156 int suppress_untranslatable,
1161 TERMTYPE save_tterm;
1169 unsigned offset = 0;
1173 if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) {
1175 if (quickdump & 1) {
1178 wrap_concat("hex:");
1179 for (n = 0; n < offset; ++n) {
1180 _nc_SPRINTF(numbuf, _nc_SLIMIT(sizeof(numbuf))
1181 "%02X", UChar(bigbuf[n]));
1182 wrap_concat(numbuf);
1185 if (quickdump & 2) {
1189 wrap_concat("b64:");
1190 for (n = 0; n < offset; ++n) {
1191 encode_b64(numbuf, bigbuf, n, &value);
1192 wrap_concat(numbuf);
1209 if (outform == F_TERMCAP || outform == F_TCONVERR) {
1210 critlen = MAX_TERMCAP_LENGTH;
1211 legend = "older termcap";
1213 set_obsolete_termcaps(tterm);
1215 critlen = MAX_TERMINFO_LENGTH;
1216 legend = "terminfo";
1220 save_sgr = set_attributes;
1222 if ((FMT_ENTRY() > critlen)
1225 save_tterm = *tterm;
1226 if (!suppress_untranslatable) {
1227 SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n",
1229 suppress_untranslatable = TRUE;
1231 if (FMT_ENTRY() > critlen) {
1233 * We pick on sgr because it's a nice long string capability that
1234 * is really just an optimization hack. Another good candidate is
1235 * acsc since it is both long and unused by BSD termcap.
1237 bool changed = FALSE;
1241 * Extended names are most likely function-key definitions. Drop
1245 for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) {
1246 const char *name = ExtStrname(tterm, (int) n, strnames);
1248 if (VALID_STRING(tterm->Strings[n])) {
1249 set_attributes = ABSENT_STRING;
1250 /* we remove long names anyway - only report the short */
1251 if (strlen(name) <= 2) {
1252 SHOW_WHY("# (%s removed to fit entry within %d bytes)\n",
1257 if (FMT_ENTRY() <= critlen)
1262 if (VALID_STRING(set_attributes)) {
1263 set_attributes = ABSENT_STRING;
1264 SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n",
1268 if (!changed || (FMT_ENTRY() > critlen)) {
1269 if (purged_acs(tterm)) {
1270 acs_chars = ABSENT_STRING;
1271 SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n",
1276 if (!changed || (FMT_ENTRY() > critlen)) {
1277 int oldversion = tversion;
1280 SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n",
1285 && kill_labels(tterm, len - critlen)) {
1286 SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n",
1291 && kill_fkeys(tterm, len - critlen)) {
1292 SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n",
1296 if (len > critlen) {
1297 (void) fprintf(stderr,
1298 "warning: %s entry is %d bytes long\n",
1299 _nc_first_name(tterm->term_names),
1301 SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n",
1304 tversion = oldversion;
1306 set_attributes = save_sgr;
1307 *tterm = save_tterm;
1309 } else if (!version_filter(STRING, STR_IDX(acs_chars))) {
1310 save_tterm = *tterm;
1311 if (purged_acs(tterm)) {
1314 *tterm = save_tterm;
1319 dump_uses(const char *name, bool infodump)
1320 /* dump "use=" clauses in the appropriate format */
1322 char buffer[MAX_TERMINFO_LENGTH];
1324 if (outform == F_TERMCAP || outform == F_TCONVERR)
1326 _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
1327 "%s%s", infodump ? "use=" : "tc=", name);
1328 wrap_concat(buffer);
1335 * Trim any remaining whitespace.
1337 if (outbuf.used != 0) {
1338 bool infodump = (outform != F_TERMCAP && outform != F_TCONVERR);
1339 char delim = (char) (infodump ? ',' : ':');
1342 for (j = (int) outbuf.used - 1; j > 0; --j) {
1343 char ch = outbuf.text[j];
1346 } else if (isspace(UChar(ch))) {
1347 outbuf.used = (size_t) j;
1348 } else if (!infodump && ch == '\\') {
1349 outbuf.used = (size_t) j;
1350 } else if (ch == delim && (j == 0 || outbuf.text[j - 1] != '\\')) {
1351 outbuf.used = (size_t) (j + 1);
1356 outbuf.text[outbuf.used] = '\0';
1358 if (outbuf.text != 0) {
1359 (void) fputs(outbuf.text, stdout);
1362 return (int) outbuf.used;
1366 compare_entry(PredHook hook,
1367 TERMTYPE *tp GCC_UNUSED,
1369 /* compare two entries */
1372 NCURSES_CONST char *name;
1375 fputs(" comparing booleans.\n", stdout);
1376 for_each_boolean(j, tp) {
1377 i = BoolIndirect(j);
1378 name = ExtBoolname(tp, (int) i, bool_names);
1380 if (isObsolete(outform, name))
1383 (*hook) (CMP_BOOLEAN, i, name);
1387 fputs(" comparing numbers.\n", stdout);
1388 for_each_number(j, tp) {
1390 name = ExtNumname(tp, (int) i, num_names);
1392 if (isObsolete(outform, name))
1395 (*hook) (CMP_NUMBER, i, name);
1399 fputs(" comparing strings.\n", stdout);
1400 for_each_string(j, tp) {
1402 name = ExtStrname(tp, (int) i, str_names);
1404 if (isObsolete(outform, name))
1407 (*hook) (CMP_STRING, i, name);
1410 /* (void) fputs(" comparing use entries.\n", stdout); */
1411 (*hook) (CMP_USE, 0, "use");
1415 #define NOTSET(s) ((s) == 0)
1418 * This bit of legerdemain turns all the terminfo variable names into
1419 * references to locations in the arrays Booleans, Numbers, and Strings ---
1420 * precisely what's needed.
1426 set_obsolete_termcaps(TERMTYPE *tp)
1428 #include "capdefaults.c"
1432 * Convert an alternate-character-set string to canonical form: sorted and
1436 repair_acsc(TERMTYPE *tp)
1438 if (VALID_STRING(acs_chars)) {
1444 bool fix_needed = FALSE;
1446 for (n = 0, source = 0; acs_chars[n] != 0; n++) {
1447 target = UChar(acs_chars[n]);
1448 if (source >= target) {
1453 if (acs_chars[n + 1])
1457 memset(mapped, 0, sizeof(mapped));
1458 for (n = 0; acs_chars[n] != 0; n++) {
1459 source = UChar(acs_chars[n]);
1460 if ((target = (unsigned char) acs_chars[n + 1]) != 0) {
1461 mapped[source] = (char) target;
1464 extra = (char) source;
1467 for (n = m = 0; n < sizeof(mapped); n++) {
1469 acs_chars[m++] = (char) n;
1470 acs_chars[m++] = mapped[n];
1474 acs_chars[m++] = extra; /* garbage in, garbage out */