X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Fdump_entry.c;h=3be09f8671cbb44cd0eb862357944798d657f5de;hp=8324fe2252286b887773707110a3d3072ee1aa9e;hb=048a1c1a65c6d98d92fca97f8d175d10d21091d0;hpb=0eb88fc5281804773e2a0c7a488a4452463535ce diff --git a/progs/dump_entry.c b/progs/dump_entry.c index 8324fe22..3be09f86 100644 --- a/progs/dump_entry.c +++ b/progs/dump_entry.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998,1999 Free Software Foundation, Inc. * + * Copyright (c) 1998-2016,2017 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -29,6 +29,7 @@ /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * + * and: Thomas E. Dickey 1996 on * ****************************************************************************/ #define __INTERNAL_CAPS_VISIBLE @@ -38,32 +39,47 @@ #include "termsort.c" /* this C file is generated */ #include /* so is this */ -MODULE_ID("$Id: dump_entry.c,v 1.37 1999/03/14 12:29:30 tom Exp $") - -#define INDENT 8 +MODULE_ID("$Id: dump_entry.c,v 1.148 2017/02/04 16:55:42 tom Exp $") #define DISCARD(string) string = ABSENT_STRING +#define PRINTF (void) printf +#define WRAPPED 32 + +#define OkIndex(index,array) ((int)(index) >= 0 && (int)(index) < (int) SIZEOF(array)) +#define TcOutput() (outform == F_TERMCAP || outform == F_TCONVERR) + +typedef struct { + char *text; + size_t used; + size_t size; +} DYNBUF; static int tversion; /* terminfo version */ static int outform; /* output format to use */ static int sortmode; /* sort mode to use */ static int width = 60; /* max line width for listings */ +static int height = 65535; /* max number of lines for listings */ static int column; /* current column, limited by 'width' */ static int oldcol; /* last value of column before wrap */ -static int tracelevel; /* level of debug output */ static bool pretty; /* true if we format if-then-else strings */ +static bool wrapped; /* true if we wrap too-long strings */ +static bool did_wrap; /* true if last wrap_concat did wrapping */ +static bool checking; /* true if we are checking for tic */ +static int quickdump; /* true if we are dumping compiled data */ -static char *outbuf; /* the output-buffer */ -static size_t out_used; /* ...its current length */ -static size_t out_size; /* ...and its allocated length */ +static char *save_sgr; + +static DYNBUF outbuf; +static DYNBUF tmpbuf; /* indirection pointers for implementing sort and display modes */ -static const int *bool_indirect, *num_indirect, *str_indirect; -static NCURSES_CONST char * const *bool_names; -static NCURSES_CONST char * const *num_names; -static NCURSES_CONST char * const *str_names; +static const PredIdx *bool_indirect, *num_indirect, *str_indirect; +static NCURSES_CONST char *const *bool_names; +static NCURSES_CONST char *const *num_names; +static NCURSES_CONST char *const *str_names; -static const char *separator, *trailer; +static const char *separator = "", *trailer = ""; +static int indent = 8; /* cover various ports and variants of terminfo */ #define V_ALLCAPS 0 /* all capabilities (SVr4, XSI, ncurses) */ @@ -72,7 +88,13 @@ static const char *separator, *trailer; #define V_AIX 3 /* AIX */ #define V_BSD 4 /* BSD */ +#if NCURSES_XNAMES +#define OBSOLETE(n) (!_nc_user_definable && (n[0] == 'O' && n[1] == 'T')) +#else #define OBSOLETE(n) (n[0] == 'O' && n[1] == 'T') +#endif + +#define isObsolete(f,n) ((f == F_TERMINFO || f == F_VARIABLE) && OBSOLETE(n)) #if NCURSES_XNAMES #define BoolIndirect(j) ((j >= BOOLCOUNT) ? (j) : ((sortmode == S_NOSORT) ? j : bool_indirect[j])) @@ -84,55 +106,117 @@ static const char *separator, *trailer; #define StrIndirect(j) ((sortmode == S_NOSORT) ? (j) : str_indirect[j]) #endif +static void failed(const char *) GCC_NORETURN; + +static void +failed(const char *s) +{ + perror(s); + ExitProgram(EXIT_FAILURE); +} + +static void +strncpy_DYN(DYNBUF * dst, const char *src, size_t need) +{ + size_t want = need + dst->used + 1; + if (want > dst->size) { + dst->size += (want + 1024); /* be generous */ + dst->text = typeRealloc(char, dst->size, dst->text); + if (dst->text == 0) + failed("strncpy_DYN"); + } + _nc_STRNCPY(dst->text + dst->used, src, need + 1); + dst->used += need; + dst->text[dst->used] = 0; +} + +static void +strcpy_DYN(DYNBUF * dst, const char *src) +{ + if (src == 0) { + dst->used = 0; + strcpy_DYN(dst, ""); + } else { + strncpy_DYN(dst, src, strlen(src)); + } +} + #if NO_LEAKS -void _nc_leaks_dump_entry(void) +static void +free_DYN(DYNBUF * p) { - if (outbuf != 0) { - free(outbuf); - outbuf = 0; - } + if (p->text != 0) + free(p->text); + p->text = 0; + p->size = 0; + p->used = 0; +} + +void +_nc_leaks_dump_entry(void) +{ + free_DYN(&outbuf); + free_DYN(&tmpbuf); } #endif -NCURSES_CONST char *nametrans(const char *name) -/* translate a capability name from termcap to terminfo */ +#define NameTrans(check,result) \ + if ((np->nte_index <= OK_ ## check) \ + && check[np->nte_index]) \ + return (result[np->nte_index]) + +NCURSES_CONST char * +nametrans(const char *name) +/* translate a capability name to termcap from terminfo */ { - const struct name_table_entry *np; + const struct name_table_entry *np; - if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) - switch(np->nte_type) - { + if ((np = _nc_find_entry(name, _nc_get_hash_table(0))) != 0) { + switch (np->nte_type) { case BOOLEAN: - if (bool_from_termcap[np->nte_index]) - return(boolcodes[np->nte_index]); + NameTrans(bool_from_termcap, boolcodes); break; case NUMBER: - if (num_from_termcap[np->nte_index]) - return(numcodes[np->nte_index]); + NameTrans(num_from_termcap, numcodes); break; case STRING: - if (str_from_termcap[np->nte_index]) - return(strcodes[np->nte_index]); + NameTrans(str_from_termcap, strcodes); break; } + } - return(0); + return (0); } -void dump_init(const char *version, int mode, int sort, int twidth, int traceval, bool formatted) +void +dump_init(const char *version, + int mode, + int sort, + bool wrap_strings, + int twidth, + int theight, + unsigned traceval, + bool formatted, + bool check, + int quick) /* set up for entry display */ { width = twidth; + height = theight; pretty = formatted; - tracelevel = traceval; + wrapped = wrap_strings; + checking = check; + quickdump = (quick & 3); + + did_wrap = (width <= 0); /* versions */ if (version == 0) tversion = V_ALLCAPS; else if (!strcmp(version, "SVr1") || !strcmp(version, "SVR1") - || !strcmp(version, "Ultrix")) + || !strcmp(version, "Ultrix")) tversion = V_SVR1; else if (!strcmp(version, "HP")) tversion = V_HPUX; @@ -144,14 +228,13 @@ void dump_init(const char *version, int mode, int sort, int twidth, int traceval tversion = V_ALLCAPS; /* implement display modes */ - switch (outform = mode) - { + switch (outform = mode) { case F_LITERAL: case F_TERMINFO: bool_names = boolnames; num_names = numnames; str_names = strnames; - separator = twidth ? ", " : ","; + separator = (twidth > 0 && theight > 1) ? ", " : ","; trailer = "\n\t"; break; @@ -159,7 +242,7 @@ void dump_init(const char *version, int mode, int sort, int twidth, int traceval bool_names = boolfnames; num_names = numfnames; str_names = strfnames; - separator = twidth ? ", " : ","; + separator = (twidth > 0 && theight > 1) ? ", " : ","; trailer = "\n\t"; break; @@ -172,10 +255,10 @@ void dump_init(const char *version, int mode, int sort, int twidth, int traceval trailer = "\\\n\t:"; break; } + indent = 8; /* implement sort modes */ - switch(sortmode = sort) - { + switch (sortmode = sort) { case S_NOSORT: if (traceval) (void) fprintf(stderr, @@ -216,161 +299,340 @@ void dump_init(const char *version, int mode, int sort, int twidth, int traceval _nc_progname, width, tversion, outform); } -static TERMTYPE *cur_type; +static TERMTYPE *cur_type; -static int dump_predicate(int type, int idx) +static int +dump_predicate(PredType type, PredIdx idx) /* predicate function to use for ordinary decompilation */ { - switch(type) { - case BOOLEAN: - return (cur_type->Booleans[idx] == FALSE) - ? FAIL : cur_type->Booleans[idx]; - - case NUMBER: - return (cur_type->Numbers[idx] == ABSENT_NUMERIC) - ? FAIL : cur_type->Numbers[idx]; - - case STRING: - return (cur_type->Strings[idx] != ABSENT_STRING) - ? (int)TRUE : FAIL; - } + switch (type) { + case BOOLEAN: + return (cur_type->Booleans[idx] == FALSE) + ? FAIL : cur_type->Booleans[idx]; + + case NUMBER: + return (cur_type->Numbers[idx] == ABSENT_NUMERIC) + ? FAIL : cur_type->Numbers[idx]; + + case STRING: + return (cur_type->Strings[idx] != ABSENT_STRING) + ? (int) TRUE : FAIL; + } - return(FALSE); /* pacify compiler */ + return (FALSE); /* pacify compiler */ } static void set_obsolete_termcaps(TERMTYPE *tp); -static void repair_acsc(TERMTYPE *tp); /* is this the index of a function key string? */ -#define FNKEY(i) (((i)<= 65 && (i)>= 75) || ((i)<= 216 && (i)>= 268)) +#define FNKEY(i) \ + (((i) >= STR_IDX(key_f0) && \ + (i) <= STR_IDX(key_f9)) || \ + ((i) >= STR_IDX(key_f11) && \ + (i) <= STR_IDX(key_f63))) + +/* + * If we configure with a different Caps file, the offsets into the arrays + * will change. So we use an address expression. + */ +#define BOOL_IDX(name) (PredType) (&(name) - &(CUR Booleans[0])) +#define NUM_IDX(name) (PredType) (&(name) - &(CUR Numbers[0])) +#define STR_IDX(name) (PredType) (&(name) - &(CUR Strings[0])) -static bool version_filter(int type, int idx) +static bool +version_filter(PredType type, PredIdx idx) /* filter out capabilities we may want to suppress */ { - switch (tversion) - { - case V_ALLCAPS: /* SVr4, XSI Curses */ - return(TRUE); + switch (tversion) { + case V_ALLCAPS: /* SVr4, XSI Curses */ + return (TRUE); - case V_SVR1: /* System V Release 1, Ultrix */ - switch (type) - { + case V_SVR1: /* System V Release 1, Ultrix */ + switch (type) { case BOOLEAN: - /* below and including xon_xoff */ - return ((idx <= 20) ? TRUE : FALSE); + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); case NUMBER: - /* below and including width_status_line */ - return ((idx <= 7) ? TRUE : FALSE); + return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); case STRING: - /* below and including prtr_non */ - return ((idx <= 144) ? TRUE : FALSE); + return ((idx <= STR_IDX(prtr_non)) ? TRUE : FALSE); } break; case V_HPUX: /* Hewlett-Packard */ - switch (type) - { + switch (type) { case BOOLEAN: - /* below and including xon_xoff */ - return ((idx <= 20) ? TRUE : FALSE); + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); case NUMBER: - /* below and including label_width */ - return ((idx <= 10) ? TRUE : FALSE); + return ((idx <= NUM_IDX(label_width)) ? TRUE : FALSE); case STRING: - if (idx <= 144) /* below and including prtr_non */ - return(TRUE); + if (idx <= STR_IDX(prtr_non)) + return (TRUE); else if (FNKEY(idx)) /* function keys */ - return(TRUE); - else if (idx==147||idx==156||idx==157) /* plab_norm,label_on,label_off */ - return(TRUE); + return (TRUE); + else if (idx == STR_IDX(plab_norm) + || idx == STR_IDX(label_on) + || idx == STR_IDX(label_off)) + return (TRUE); else - return(FALSE); + return (FALSE); } break; case V_AIX: /* AIX */ - switch (type) - { + switch (type) { case BOOLEAN: - /* below and including xon_xoff */ - return ((idx <= 20) ? TRUE : FALSE); + return ((idx <= BOOL_IDX(xon_xoff)) ? TRUE : FALSE); case NUMBER: - /* below and including width_status_line */ - return ((idx <= 7) ? TRUE : FALSE); + return ((idx <= NUM_IDX(width_status_line)) ? TRUE : FALSE); case STRING: - if (idx <= 144) /* below and including prtr_non */ - return(TRUE); + if (idx <= STR_IDX(prtr_non)) + return (TRUE); else if (FNKEY(idx)) /* function keys */ - return(TRUE); + return (TRUE); else - return(FALSE); + return (FALSE); } break; +#define is_termcap(type) (OkIndex(idx, type##_from_termcap) && \ + type##_from_termcap[idx]) + case V_BSD: /* BSD */ - switch (type) - { + switch (type) { case BOOLEAN: - return bool_from_termcap[idx]; + return is_termcap(bool); case NUMBER: - return num_from_termcap[idx]; + return is_termcap(num); case STRING: - return str_from_termcap[idx]; + return is_termcap(str); } break; } - return(FALSE); /* pacify the compiler */ + return (FALSE); /* pacify the compiler */ +} + +static void +trim_trailing(void) +{ + while (outbuf.used > 0 && outbuf.text[outbuf.used - 1] == ' ') + outbuf.text[--outbuf.used] = '\0'; +} + +static void +force_wrap(void) +{ + oldcol = column; + trim_trailing(); + strcpy_DYN(&outbuf, trailer); + column = indent; } -static -void append_output (const char *src) +static int +op_length(const char *src, int offset) { - if (src == 0) { - out_used = 0; - append_output(""); + int result = 0; + int ch; + if (offset > 0 && src[offset - 1] == '\\') { + result = 0; + } else { + result++; /* for '%' mark */ + ch = src[offset + result]; + if (TcOutput()) { + if (ch == '>') { + result += 3; + } else if (ch == '+') { + result += 2; + } else { + result++; + } + } else if (ch == '\'') { + result += 3; + } else if (ch == L_CURL[0]) { + int n = result; + while ((ch = src[offset + n]) != '\0') { + if (ch == R_CURL[0]) { + result = ++n; + break; + } + n++; + } + } else if (strchr("pPg", ch) != 0) { + result += 2; } else { - size_t need = strlen(src); - size_t want = need + out_used + 1; - if (want > out_size) { - out_size += want; /* be generous */ - if (outbuf == 0) - outbuf = malloc(out_size); - else - outbuf = realloc(outbuf, out_size); + result++; /* ordinary operator */ + } + } + return result; +} + +/* + * When wrapping too-long strings, avoid splitting a backslash sequence, or + * a terminfo '%' operator. That will leave things a little ragged, but avoids + * a stray backslash at the end of the line, as well as making the result a + * little more readable. + */ +static int +find_split(const char *src, int step, int size) +{ + int result = size; + int n; + if (size > 0) { + /* check if that would split a backslash-sequence */ + int mark = size; + for (n = size - 1; n > 0; --n) { + int ch = UChar(src[step + n]); + if (ch == '\\') { + if (n > 0 && src[step + n - 1] == ch) + --n; + mark = n; + break; + } else if (!isalnum(ch)) { + break; + } + } + if (mark < size) { + result = mark; + } else { + /* check if that would split a backslash-sequence */ + for (n = size - 1; n > 0; --n) { + int ch = UChar(src[step + n]); + if (ch == '%') { + int need = op_length(src, step + n); + if ((n + need) > size) + mark = n; + break; } - (void)strcpy(outbuf + out_used, src); - out_used += need; + } + if (mark < size) { + result = mark; + } } + } + return result; } -static -void force_wrap(void) +/* + * If we are going to wrap lines, we cannot leave literal spaces because that + * would be ambiguous if we split on that space. + */ +static char * +fill_spaces(const char *src) { - oldcol = column; - append_output(trailer); - column = INDENT; + const char *fill = "\\s"; + size_t need = strlen(src); + size_t size = strlen(fill); + char *result = 0; + int pass; + int s, d; + for (pass = 0; pass < 2; ++pass) { + for (s = d = 0; src[s] != '\0'; ++s) { + if (src[s] == ' ') { + if (pass) { + strcpy(&result[d], fill); + d += (int) size; + } else { + need += size; + } + } else { + if (pass) { + result[d++] = src[s]; + } else { + ++d; + } + } + } + if (pass) { + result[d] = '\0'; + } else { + result = malloc(need + 1); + if (result == 0) + failed("fill_spaces"); + } + } + return result; } -static -void wrap_concat(const char *src) +static void +wrap_concat(const char *src) { - int need = strlen(src); - int want = strlen(separator) + need; + int need = (int) strlen(src); + int gaps = (int) strlen(separator); + int want = gaps + need; - if (column > INDENT - && column + want > width) { - force_wrap(); + did_wrap = (width <= 0); + if (column > indent + && column + want > width) { + force_wrap(); + } + if (wrapped && + (width >= 0) && + (column + want) > width && + (!TcOutput() || strncmp(src, "..", 2))) { + int step = 0; + int used = width > WRAPPED ? width : WRAPPED; + int size = used; + int base = 0; + char *p, align[9]; + const char *my_t = trailer; + char *fill = fill_spaces(src); + int last = (int) strlen(fill); + + need = last; + + if (TcOutput()) + trailer = "\\\n\t "; + + if ((p = strchr(fill, '=')) != 0) { + base = (int) (p + 1 - fill); + if (base > 8) + base = 8; + _nc_SPRINTF(align, _nc_SLIMIT(align) "%*s", base, " "); + } else { + align[base] = '\0'; } - append_output(src); - append_output(separator); + /* "pretty" overrides wrapping if it already split the line */ + if (!pretty || strchr(fill, '\n') == 0) { + while ((column + (need + gaps)) > used) { + size = used; + if (step) { + strcpy_DYN(&outbuf, align); + size -= base; + } + if (size > (last - step)) { + size = (last - step); + } + size = find_split(fill, step, size); + strncpy_DYN(&outbuf, fill + step, (size_t) size); + step += size; + need -= size; + if (need > 0) { + force_wrap(); + did_wrap = TRUE; + } + } + } + if (need > 0) { + if (step) + strcpy_DYN(&outbuf, align); + strcpy_DYN(&outbuf, fill + step); + } + strcpy_DYN(&outbuf, separator); + trailer = my_t; + force_wrap(); + + free(fill); + } else { + strcpy_DYN(&outbuf, src); + strcpy_DYN(&outbuf, separator); column += need; + } } #define IGNORE_SEP_TRAIL(first,last,sep_trail) \ if ((size_t)(last - first) > sizeof(sep_trail)-1 \ && !strncmp(first, sep_trail, sizeof(sep_trail)-1)) \ - first += sizeof(sep_trail)-2 + first += sizeof(sep_trail)-2 /* Returns the nominal length of the buffer assuming it is termcap format, * i.e., the continuation sequence is treated as a single character ":". @@ -382,98 +644,178 @@ void wrap_concat(const char *src) * of the best fixed-buffer implementation; the worst case may be much worse. */ #ifdef TEST_TERMCAP_LENGTH -static int termcap_length(const char *src) +static int +termcap_length(const char *src) { - static const char pattern[] = ":\\\n\t:"; + static const char pattern[] = ":\\\n\t:"; - int len = 0; - const char *const t = src + strlen(src); + int len = 0; + const char *const t = src + strlen(src); - while (*src != '\0') { - IGNORE_SEP_TRAIL(src, t, pattern); - src++; - len++; - } - return len; + while (*src != '\0') { + IGNORE_SEP_TRAIL(src, t, pattern); + src++; + len++; + } + return len; } #else #define termcap_length(src) strlen(src) #endif -static char * fmt_complex(char *dst, char *src, int level) +static void +indent_DYN(DYNBUF * buffer, int level) { - int percent = 0; - int n; + int n; - dst += strlen(dst); - while (*src != '\0') { - switch (*src) { - case '\\': - percent = 0; - *dst++ = *src++; - break; - case '%': - percent = 1; - break; - case '?': /* "if" */ - case 't': /* "then" */ - case 'e': /* "else" */ - if (percent) { - percent = 0; - dst[-1] = '\n'; - for (n = 0; n <= level; n++) - *dst++ = '\t'; - *dst++ = '%'; - *dst++ = *src; - *dst = '\0'; - if (*src++ == '?') { - src = fmt_complex(dst, src, level+1); - dst += strlen(dst); - } else if (level == 1) { - _nc_warning("%%%c without %%?", *src); - } - continue; - } - break; - case ';': /* "endif" */ - if (percent) { - percent = 0; - if (level > 1) { - dst[-1] = '\n'; - for (n = 0; n < level; n++) - *dst++ = '\t'; - *dst++ = '%'; - *dst++ = *src++; - *dst = '\0'; - return src; - } - _nc_warning("%%; without %%?"); + for (n = 0; n < level; n++) + strncpy_DYN(buffer, "\t", (size_t) 1); +} + +bool +has_params(const char *src) +{ + bool result = FALSE; + int len = (int) strlen(src); + int n; + bool ifthen = FALSE; + bool params = FALSE; + + for (n = 0; n < len - 1; ++n) { + if (!strncmp(src + n, "%p", (size_t) 2)) { + params = TRUE; + } else if (!strncmp(src + n, "%;", (size_t) 2)) { + ifthen = TRUE; + result = params; + break; + } + } + if (!ifthen) { + result = ((len > 50) && params); + } + return result; +} + +static char * +fmt_complex(TERMTYPE *tterm, const char *capability, char *src, int level) +{ + bool percent = FALSE; + bool params = has_params(src); + + while (*src != '\0') { + switch (*src) { + case '^': + percent = FALSE; + strncpy_DYN(&tmpbuf, src++, (size_t) 1); + break; + case '\\': + percent = FALSE; + strncpy_DYN(&tmpbuf, src++, (size_t) 1); + break; + case '%': + percent = TRUE; + break; + case '?': /* "if" */ + case 't': /* "then" */ + case 'e': /* "else" */ + if (percent) { + percent = FALSE; + tmpbuf.text[tmpbuf.used - 1] = '\n'; + /* treat a "%e" as else-if, on the same level */ + if (*src == 'e') { + indent_DYN(&tmpbuf, level); + strncpy_DYN(&tmpbuf, "%", (size_t) 1); + strncpy_DYN(&tmpbuf, src, (size_t) 1); + src++; + params = has_params(src); + if (!params && *src != '\0' && *src != '%') { + strncpy_DYN(&tmpbuf, "\n", (size_t) 1); + indent_DYN(&tmpbuf, level + 1); + } + } else { + indent_DYN(&tmpbuf, level + 1); + strncpy_DYN(&tmpbuf, "%", (size_t) 1); + strncpy_DYN(&tmpbuf, src, (size_t) 1); + if (*src++ == '?') { + src = fmt_complex(tterm, capability, src, level + 1); + if (*src != '\0' && *src != '%') { + strncpy_DYN(&tmpbuf, "\n", (size_t) 1); + indent_DYN(&tmpbuf, level + 1); } - break; - default: - percent = 0; - break; + } else if (level == 1) { + if (checking) + _nc_warning("%s: %%%c without %%? in %s", + _nc_first_name(tterm->term_names), + *src, capability); + } } - *dst++ = *src++; + continue; + } + break; + case ';': /* "endif" */ + if (percent) { + percent = FALSE; + if (level > 1) { + tmpbuf.text[tmpbuf.used - 1] = '\n'; + indent_DYN(&tmpbuf, level); + strncpy_DYN(&tmpbuf, "%", (size_t) 1); + strncpy_DYN(&tmpbuf, src++, (size_t) 1); + if (src[0] == '%' + && src[1] != '\0' + && (strchr("?e;", src[1])) == 0) { + tmpbuf.text[tmpbuf.used++] = '\n'; + indent_DYN(&tmpbuf, level); + } + return src; + } + if (checking) + _nc_warning("%s: %%; without %%? in %s", + _nc_first_name(tterm->term_names), + capability); + } + break; + case 'p': + if (percent && params) { + tmpbuf.text[tmpbuf.used - 1] = '\n'; + indent_DYN(&tmpbuf, level + 1); + strncpy_DYN(&tmpbuf, "%", (size_t) 1); + } + params = FALSE; + percent = FALSE; + break; + case ' ': + strncpy_DYN(&tmpbuf, "\\s", (size_t) 2); + ++src; + continue; + default: + percent = FALSE; + break; } - *dst = '\0'; - return src; + strncpy_DYN(&tmpbuf, src++, (size_t) 1); + } + return src; } -int fmt_entry(TERMTYPE *tterm, - int (*pred)(int type, int idx), - bool suppress_untranslatable, - bool infodump, - int numbers) +#define SAME_CAP(n,cap) (&tterm->Strings[n] == &cap) +#define EXTRA_CAP 20 + +int +fmt_entry(TERMTYPE *tterm, + PredFunc pred, + int content_only, + int suppress_untranslatable, + int infodump, + int numbers) { -int i, j; -char buffer[MAX_TERMINFO_LENGTH]; -NCURSES_CONST char *name; -int predval, len; -int num_bools = 0; -int num_values = 0; -int num_strings = 0; -bool outcount = 0; + PredIdx i, j; + char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP]; + char *capability; + NCURSES_CONST char *name; + int predval, len; + PredIdx num_bools = 0; + PredIdx num_values = 0; + PredIdx num_strings = 0; + bool outcount = 0; #define WRAP_CONCAT \ wrap_concat(buffer); \ @@ -486,52 +828,72 @@ bool outcount = 0; pred = dump_predicate; } - append_output(0); - append_output(tterm->term_names); - append_output(separator); - column = out_used; - force_wrap(); + strcpy_DYN(&outbuf, 0); + if (content_only) { + column = indent; /* FIXME: workaround to prevent empty lines */ + } else { + strcpy_DYN(&outbuf, tterm->term_names); - for_each_boolean(j,tterm) { + /* + * Colon is legal in terminfo descriptions, but not in termcap. + */ + if (!infodump) { + char *p = outbuf.text; + while (*p) { + if (*p == ':') { + *p = '='; + } + ++p; + } + } + strcpy_DYN(&outbuf, separator); + column = (int) outbuf.used; + if (height > 1) + force_wrap(); + } + + for_each_boolean(j, tterm) { i = BoolIndirect(j); - name = ExtBoolname(tterm,i,bool_names); + name = ExtBoolname(tterm, (int) i, bool_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); if (!version_filter(BOOLEAN, i)) continue; - else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + else if (isObsolete(outform, name)) continue; predval = pred(BOOLEAN, i); if (predval != FAIL) { - (void) strcpy(buffer, name); + _nc_STRCPY(buffer, name, sizeof(buffer)); if (predval <= 0) - (void) strcat(buffer, "@"); + _nc_STRCAT(buffer, "@", sizeof(buffer)); else if (i + 1 > num_bools) num_bools = i + 1; WRAP_CONCAT; } } - if (column != INDENT) + if (column != indent && height > 1) force_wrap(); - for_each_number(j,tterm) { + for_each_number(j, tterm) { i = NumIndirect(j); - name = ExtNumname(tterm,i,num_names); + name = ExtNumname(tterm, (int) i, num_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); if (!version_filter(NUMBER, i)) continue; - else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + else if (isObsolete(outform, name)) continue; predval = pred(NUMBER, i); if (predval != FAIL) { if (tterm->Numbers[i] < 0) { - sprintf(buffer, "%s@", name); + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s@", name); } else { - sprintf(buffer, "%s#%d", name, tterm->Numbers[i]); + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s#%d", name, tterm->Numbers[i]); if (i + 1 > num_values) num_values = i + 1; } @@ -539,151 +901,242 @@ bool outcount = 0; } } - if (column != INDENT) + if (column != indent && height > 1) force_wrap(); - len += num_bools - + num_values * 2 - + strlen(tterm->term_names) + 1; + len += (int) (num_bools + + num_values * 2 + + strlen(tterm->term_names) + 1); if (len & 1) - len++; + len++; + +#undef CUR +#define CUR tterm-> + if (outform == F_TERMCAP) { + if (termcap_reset != ABSENT_STRING) { + if (init_3string != ABSENT_STRING + && !strcmp(init_3string, termcap_reset)) + DISCARD(init_3string); + + if (reset_2string != ABSENT_STRING + && !strcmp(reset_2string, termcap_reset)) + DISCARD(reset_2string); + } + } - repair_acsc(tterm); for_each_string(j, tterm) { i = StrIndirect(j); - name = ExtStrname(tterm,i,str_names); + name = ExtStrname(tterm, (int) i, str_names); + assert(strlen(name) < sizeof(buffer) - EXTRA_CAP); + + capability = tterm->Strings[i]; if (!version_filter(STRING, i)) continue; - else if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + else if (isObsolete(outform, name)) continue; +#if NCURSES_XNAMES /* - * Some older versions of vi want rmir/smir to be defined - * for ich/ich1 to work. If they're not defined, force - * them to be output as defined and empty. + * Extended names can be longer than 2 characters, but termcap programs + * cannot read those (filter them out). */ - if (outform==F_TERMCAP) - { -#undef CUR -#define CUR tterm-> - if (insert_character || parm_ich) - { - if (&tterm->Strings[i] == &enter_insert_mode - && enter_insert_mode == ABSENT_STRING) - { - (void) strcpy(buffer, "im="); - goto catenate; + if (outform == F_TERMCAP && (strlen(name) > 2)) + continue; +#endif + + if (outform == F_TERMCAP) { + /* + * Some older versions of vi want rmir/smir to be defined + * for ich/ich1 to work. If they're not defined, force + * them to be output as defined and empty. + */ + if (PRESENT(insert_character) || PRESENT(parm_ich)) { + if (SAME_CAP(i, enter_insert_mode) + && enter_insert_mode == ABSENT_STRING) { + _nc_STRCPY(buffer, "im=", sizeof(buffer)); + WRAP_CONCAT; + continue; } - if (&tterm->Strings[i] == &exit_insert_mode - && exit_insert_mode == ABSENT_STRING) - { - (void) strcpy(buffer, "ei="); - goto catenate; + if (SAME_CAP(i, exit_insert_mode) + && exit_insert_mode == ABSENT_STRING) { + _nc_STRCPY(buffer, "ei=", sizeof(buffer)); + WRAP_CONCAT; + continue; } } + /* + * termcap applications such as screen will be confused if sgr0 + * is translated to a string containing rmacs. Filter that out. + */ + if (PRESENT(exit_attribute_mode)) { + if (SAME_CAP(i, exit_attribute_mode)) { + char *trimmed_sgr0; + char *my_sgr = set_attributes; + + set_attributes = save_sgr; + + trimmed_sgr0 = _nc_trim_sgr0(tterm); + if (strcmp(capability, trimmed_sgr0)) + capability = trimmed_sgr0; + else { + if (trimmed_sgr0 != exit_attribute_mode) + free(trimmed_sgr0); + } - if (init_3string != 0 - && termcap_reset != 0 - && !strcmp(init_3string, termcap_reset)) - DISCARD(init_3string); - - if (reset_2string != 0 - && termcap_reset != 0 - && !strcmp(reset_2string, termcap_reset)) - DISCARD(reset_2string); + set_attributes = my_sgr; + } + } } predval = pred(STRING, i); buffer[0] = '\0'; + if (predval != FAIL) { - if (tterm->Strings[i] != ABSENT_STRING - && i + 1 > num_strings) + if (capability != ABSENT_STRING + && i + 1 > num_strings) num_strings = i + 1; - if (!VALID_STRING(tterm->Strings[i])) - sprintf(buffer, "%s@", name); - else if (outform == F_TERMCAP || outform == F_TCONVERR) - { - char *srccap = _nc_tic_expand(tterm->Strings[i], FALSE, numbers); - char *cv = _nc_infotocap(name, srccap, parametrized[i]); - - if (cv == 0) - { - if (outform == F_TCONVERR) - sprintf(buffer, "%s=!!! %s WILL NOT CONVERT !!!", name, srccap); - else if (suppress_untranslatable) + + if (!VALID_STRING(capability)) { + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s@", name); + WRAP_CONCAT; + } else if (TcOutput()) { + char *srccap = _nc_tic_expand(capability, TRUE, numbers); + int params = (((i < (int) SIZEOF(parametrized)) && + (i < STRCOUNT)) + ? parametrized[i] + : ((*srccap == 'k') + ? 0 + : has_params(srccap))); + char *cv = _nc_infotocap(name, srccap, params); + + if (cv == 0) { + if (outform == F_TCONVERR) { + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s=!!! %s WILL NOT CONVERT !!!", + name, srccap); + } else if (suppress_untranslatable) { continue; - else - sprintf(buffer, "..%s=%s", name, srccap); + } else { + char *s = srccap, *d = buffer; + _nc_SPRINTF(d, _nc_SLIMIT(sizeof(buffer)) "..%s=", name); + d += strlen(d); + while ((*d = *s++) != 0) { + if (*d == ':') { + *d++ = '\\'; + *d = ':'; + } else if (*d == '\\') { + *++d = *s++; + } + d++; + } + } + } else { + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s=%s", name, cv); } - else - sprintf(buffer, "%s=%s", name, cv); - len += strlen(tterm->Strings[i]) + 1; - } - else - { - char *src = _nc_tic_expand(tterm->Strings[i], outform==F_TERMINFO, numbers); - sprintf(buffer, "%s=", name); - if (pretty && outform==F_TERMINFO) - fmt_complex(buffer + strlen(buffer), src, 1); - else - strcat(buffer, src); - len += strlen(tterm->Strings[i]) + 1; + len += (int) strlen(capability) + 1; + WRAP_CONCAT; + } else { + char *src = _nc_tic_expand(capability, + outform == F_TERMINFO, numbers); + + strcpy_DYN(&tmpbuf, 0); + strcpy_DYN(&tmpbuf, name); + strcpy_DYN(&tmpbuf, "="); + if (pretty + && (outform == F_TERMINFO + || outform == F_VARIABLE)) { + fmt_complex(tterm, name, src, 1); + } else { + strcpy_DYN(&tmpbuf, src); + } + len += (int) strlen(capability) + 1; + wrap_concat(tmpbuf.text); + outcount = TRUE; } - - catenate: - WRAP_CONCAT; } + /* e.g., trimmed_sgr0 */ + if (capability != ABSENT_STRING && + capability != CANCELLED_STRING && + capability != tterm->Strings[i]) + free(capability); } - len += num_strings * 2; + len += (int) (num_strings * 2); /* * This piece of code should be an effective inverse of the functions - * postprocess_terminfo and postprocess_terminfo in parse_entry.c. + * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c. * Much more work should be done on this to support dumping termcaps. */ - if (tversion == V_HPUX) - { - if (memory_lock) - { - (void) sprintf(buffer, "meml=%s", memory_lock); + if (tversion == V_HPUX) { + if (VALID_STRING(memory_lock)) { + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "meml=%s", memory_lock); WRAP_CONCAT; } - if (memory_unlock) - { - (void) sprintf(buffer, "memu=%s", memory_unlock); + if (VALID_STRING(memory_unlock)) { + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "memu=%s", memory_unlock); WRAP_CONCAT; } - } - else if (tversion == V_AIX) - { - if (VALID_STRING(acs_chars)) - { - bool box_ok = TRUE; - const char *acstrans = "lqkxjmwuvtn"; - const char *cp; - char *tp, *sp, boxchars[11]; + } else if (tversion == V_AIX) { + if (VALID_STRING(acs_chars)) { + bool box_ok = TRUE; + const char *acstrans = "lqkxjmwuvtn"; + const char *cp; + char *tp, *sp, boxchars[11]; tp = boxchars; - for (cp = acstrans; *cp; cp++) - { - sp = strchr(acs_chars, *cp); + for (cp = acstrans; *cp; cp++) { + sp = (strchr) (acs_chars, *cp); if (sp) *tp++ = sp[1]; - else - { + else { box_ok = FALSE; break; } } tp[0] = '\0'; - if (box_ok) - { - (void) strcpy(buffer, "box1="); - (void) strcat(buffer, _nc_tic_expand(boxchars, outform==F_TERMINFO, numbers)); + if (box_ok) { + char *tmp = _nc_tic_expand(boxchars, + (outform == F_TERMINFO), + numbers); + _nc_STRCPY(buffer, "box1=", sizeof(buffer)); + while (*tmp != '\0') { + size_t have = strlen(buffer); + size_t next = strlen(tmp); + size_t want = have + next + 1; + size_t last = next; + char save = '\0'; + + /* + * If the expanded string is too long for the buffer, + * chop it off and save the location where we chopped it. + */ + if (want >= sizeof(buffer)) { + save = tmp[last]; + tmp[last] = '\0'; + } + _nc_STRCAT(buffer, tmp, sizeof(buffer)); + + /* + * If we chopped the buffer, replace the missing piece and + * shift everything to append the remainder. + */ + if (save != '\0') { + next = 0; + tmp[last] = save; + while ((tmp[next] = tmp[last + next]) != '\0') { + ++next; + } + } else { + break; + } + } WRAP_CONCAT; } } @@ -693,30 +1146,36 @@ bool outcount = 0; * kludge: trim off trailer to avoid an extra blank line * in infocmp -u output when there are no string differences */ - if (outcount) - { - j = out_used; - if (j >= 2 - && outbuf[j-1] == '\t' - && outbuf[j-2] == '\n') { - out_used -= 2; + if (outcount) { + bool trimmed = FALSE; + j = (PredIdx) outbuf.used; + if (wrapped && did_wrap) { + /* EMPTY */ ; + } else if (j >= 2 + && outbuf.text[j - 1] == '\t' + && outbuf.text[j - 2] == '\n') { + outbuf.used -= 2; + trimmed = TRUE; } else if (j >= 4 - && outbuf[j-1] == ':' - && outbuf[j-2] == '\t' - && outbuf[j-3] == '\n' - && outbuf[j-4] == '\\') { - out_used -= 4; + && outbuf.text[j - 1] == ':' + && outbuf.text[j - 2] == '\t' + && outbuf.text[j - 3] == '\n' + && outbuf.text[j - 4] == '\\') { + outbuf.used -= 4; + trimmed = TRUE; + } + if (trimmed) { + outbuf.text[outbuf.used] = '\0'; + column = oldcol; + strcpy_DYN(&outbuf, " "); } - outbuf[out_used] = '\0'; - column = oldcol; } - #if 0 fprintf(stderr, "num_bools = %d\n", num_bools); fprintf(stderr, "num_values = %d\n", num_values); fprintf(stderr, "num_strings = %d\n", num_strings); fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n", - tterm->term_names, len, out_used, outbuf); + tterm->term_names, len, outbuf.used, outbuf.text); #endif /* * Here's where we use infodump to trigger a more stringent length check @@ -725,128 +1184,434 @@ bool outcount = 0; * It gives an idea of which entries are deadly to even *scan past*, * as opposed to *use*. */ - return(infodump ? len : termcap_length(outbuf)); + return (infodump ? len : (int) termcap_length(outbuf.text)); +} + +static bool +kill_string(TERMTYPE *tterm, char *cap) +{ + unsigned n; + for (n = 0; n < NUM_STRINGS(tterm); ++n) { + if (cap == tterm->Strings[n]) { + tterm->Strings[n] = ABSENT_STRING; + return TRUE; + } + } + return FALSE; +} + +static char * +find_string(TERMTYPE *tterm, char *name) +{ + PredIdx n; + for (n = 0; n < NUM_STRINGS(tterm); ++n) { + if (version_filter(STRING, n) + && !strcmp(name, strnames[n])) { + char *cap = tterm->Strings[n]; + if (VALID_STRING(cap)) { + return cap; + } + break; + } + } + return ABSENT_STRING; +} + +/* + * This is used to remove function-key labels from a termcap entry to + * make it smaller. + */ +static int +kill_labels(TERMTYPE *tterm, int target) +{ + int n; + int result = 0; + char *cap; + char name[10]; + + for (n = 0; n <= 10; ++n) { + _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "lf%d", n); + if ((cap = find_string(tterm, name)) != ABSENT_STRING + && kill_string(tterm, cap)) { + target -= (int) (strlen(cap) + 5); + ++result; + if (target < 0) + break; + } + } + return result; +} + +/* + * This is used to remove function-key definitions from a termcap entry to + * make it smaller. + */ +static int +kill_fkeys(TERMTYPE *tterm, int target) +{ + int n; + int result = 0; + char *cap; + char name[10]; + + for (n = 60; n >= 0; --n) { + _nc_SPRINTF(name, _nc_SLIMIT(sizeof(name)) "kf%d", n); + if ((cap = find_string(tterm, name)) != ABSENT_STRING + && kill_string(tterm, cap)) { + target -= (int) (strlen(cap) + 5); + ++result; + if (target < 0) + break; + } + } + return result; +} + +/* + * Check if the given acsc string is a 1-1 mapping, i.e., just-like-vt100. + * Also, since this is for termcap, we only care about the line-drawing map. + */ +#define isLine(c) (strchr("lmkjtuvwqxn", c) != 0) + +static bool +one_one_mapping(const char *mapping) +{ + bool result = TRUE; + + if (mapping != ABSENT_STRING) { + int n = 0; + while (mapping[n] != '\0') { + if (isLine(mapping[n]) && + mapping[n] != mapping[n + 1]) { + result = FALSE; + break; + } + n += 2; + } + } + return result; +} + +#define FMT_ENTRY() \ + fmt_entry(tterm, pred, \ + 0, \ + suppress_untranslatable, \ + infodump, numbers) + +#define SHOW_WHY PRINTF + +static bool +purged_acs(TERMTYPE *tterm) +{ + bool result = FALSE; + + if (VALID_STRING(acs_chars)) { + if (!one_one_mapping(acs_chars)) { + enter_alt_charset_mode = ABSENT_STRING; + exit_alt_charset_mode = ABSENT_STRING; + SHOW_WHY("# (rmacs/smacs removed for consistency)\n"); + } + result = TRUE; + } + return result; } -int dump_entry(TERMTYPE *tterm, bool limited, int numbers, int (*pred)(int type, int idx)) -/* dump a single entry */ +static void +encode_b64(char *target, char *source, unsigned state, int *saved) { - int len, critlen; - const char *legend; - bool infodump; + /* RFC-4648 */ + static const char data[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" "-_"; + int ch = UChar(source[state]); + + switch (state % 3) { + case 0: + *target++ = data[(ch >> 2) & 077]; + *saved = (ch << 4); + break; + case 1: + *target++ = data[((ch >> 4) | *saved) & 077]; + *saved = (ch << 2); + break; + case 2: + *target++ = data[((ch >> 6) | *saved) & 077]; + *target++ = data[ch & 077]; + *saved = 0; + break; + } + *target = '\0'; +} - if (outform==F_TERMCAP || outform==F_TCONVERR) - { +/* + * Dump a single entry. + */ +void +dump_entry(TERMTYPE *tterm, + int suppress_untranslatable, + int limited, + int numbers, + PredFunc pred) +{ + TERMTYPE save_tterm; + int len, critlen; + const char *legend; + bool infodump; + + if (quickdump) { + char bigbuf[65536]; + unsigned n; + unsigned offset = 0; + separator = ""; + trailer = "\n"; + indent = 0; + if (_nc_write_object(tterm, bigbuf, &offset, sizeof(bigbuf)) == OK) { + char numbuf[80]; + if (quickdump & 1) { + if (outbuf.used) + wrap_concat("\n"); + wrap_concat("hex:"); + for (n = 0; n < offset; ++n) { + _nc_SPRINTF(numbuf, _nc_SLIMIT(sizeof(numbuf)) + "%02X", UChar(bigbuf[n])); + wrap_concat(numbuf); + } + } + if (quickdump & 2) { + static char padding[] = + {0, 0}; + int value = 0; + if (outbuf.used) + wrap_concat("\n"); + wrap_concat("b64:"); + for (n = 0; n < offset; ++n) { + encode_b64(numbuf, bigbuf, n, &value); + wrap_concat(numbuf); + } + switch (n % 3) { + case 0: + break; + case 1: + encode_b64(numbuf, padding, 1, &value); + wrap_concat(numbuf); + wrap_concat("=="); + break; + case 2: + encode_b64(numbuf, padding, 1, &value); + wrap_concat(numbuf); + wrap_concat("="); + break; + } + } + } + return; + } + + if (TcOutput()) { critlen = MAX_TERMCAP_LENGTH; legend = "older termcap"; infodump = FALSE; set_obsolete_termcaps(tterm); - } - else - { + } else { critlen = MAX_TERMINFO_LENGTH; legend = "terminfo"; infodump = TRUE; } - if (((len = fmt_entry(tterm, pred, FALSE, infodump, numbers)) > critlen) && limited) - { - (void) printf("# (untranslatable capabilities removed to fit entry within %d bytes)\n", - critlen); - if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) - { + save_sgr = set_attributes; + + if ((FMT_ENTRY() > critlen) + && limited) { + + save_tterm = *tterm; + if (!suppress_untranslatable) { + SHOW_WHY("# (untranslatable capabilities removed to fit entry within %d bytes)\n", + critlen); + suppress_untranslatable = TRUE; + } + if (FMT_ENTRY() > critlen) { /* * We pick on sgr because it's a nice long string capability that - * is really just an optimization hack. + * is really just an optimization hack. Another good candidate is + * acsc since it is both long and unused by BSD termcap. + */ + bool changed = FALSE; + +#if NCURSES_XNAMES + /* + * Extended names are most likely function-key definitions. Drop + * those first. */ - char *oldsgr = set_attributes; - set_attributes = ABSENT_STRING; - (void) printf("# (sgr removed to fit entry within %d bytes)\n", - critlen); - if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) - { + unsigned n; + for (n = STRCOUNT; n < NUM_STRINGS(tterm); n++) { + const char *name = ExtStrname(tterm, (int) n, strnames); + + if (VALID_STRING(tterm->Strings[n])) { + set_attributes = ABSENT_STRING; + /* we remove long names anyway - only report the short */ + if (strlen(name) <= 2) { + SHOW_WHY("# (%s removed to fit entry within %d bytes)\n", + name, + critlen); + } + changed = TRUE; + if (FMT_ENTRY() <= critlen) + break; + } + } +#endif + if (VALID_STRING(set_attributes)) { + set_attributes = ABSENT_STRING; + SHOW_WHY("# (sgr removed to fit entry within %d bytes)\n", + critlen); + changed = TRUE; + } + if (!changed || (FMT_ENTRY() > critlen)) { + if (purged_acs(tterm)) { + acs_chars = ABSENT_STRING; + SHOW_WHY("# (acsc removed to fit entry within %d bytes)\n", + critlen); + changed = TRUE; + } + } + if (!changed || (FMT_ENTRY() > critlen)) { int oldversion = tversion; tversion = V_BSD; - (void) printf("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", - critlen); - - if ((len = fmt_entry(tterm, pred, TRUE, infodump, numbers)) > critlen) - { + SHOW_WHY("# (terminfo-only capabilities suppressed to fit entry within %d bytes)\n", + critlen); + + len = FMT_ENTRY(); + if (len > critlen + && kill_labels(tterm, len - critlen)) { + SHOW_WHY("# (some labels capabilities suppressed to fit entry within %d bytes)\n", + critlen); + len = FMT_ENTRY(); + } + if (len > critlen + && kill_fkeys(tterm, len - critlen)) { + SHOW_WHY("# (some function-key capabilities suppressed to fit entry within %d bytes)\n", + critlen); + len = FMT_ENTRY(); + } + if (len > critlen) { (void) fprintf(stderr, - "warning: %s entry is %d bytes long\n", - _nc_first_name(tterm->term_names), - len); - (void) printf( - "# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", - len, legend); + "warning: %s entry is %d bytes long\n", + _nc_first_name(tterm->term_names), + len); + SHOW_WHY("# WARNING: this entry, %d bytes long, may core-dump %s libraries!\n", + len, legend); } tversion = oldversion; } - set_attributes = oldsgr; + set_attributes = save_sgr; + *tterm = save_tterm; } + } else if (!version_filter(STRING, STR_IDX(acs_chars))) { + save_tterm = *tterm; + if (purged_acs(tterm)) { + (void) FMT_ENTRY(); + } + *tterm = save_tterm; } - - (void) fputs(outbuf, stdout); - return len; } -int dump_uses(const char *name, bool infodump) +void +dump_uses(const char *name, bool infodump) /* dump "use=" clauses in the appropriate format */ { char buffer[MAX_TERMINFO_LENGTH]; - append_output(0); - (void)sprintf(buffer, "%s%s", infodump ? "use=" : "tc=", name); + if (TcOutput()) + trim_trailing(); + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) + "%s%s", infodump ? "use=" : "tc=", name); wrap_concat(buffer); - (void) fputs(outbuf, stdout); - return out_used; } -void compare_entry(void (*hook)(int t, int i, const char *name), TERMTYPE *tp GCC_UNUSED) +int +show_entry(void) +{ + /* + * Trim any remaining whitespace. + */ + if (outbuf.used != 0) { + bool infodump = !TcOutput(); + char delim = (char) (infodump ? ',' : ':'); + int j; + + for (j = (int) outbuf.used - 1; j > 0; --j) { + char ch = outbuf.text[j]; + if (ch == '\n') { + ; + } else if (isspace(UChar(ch))) { + outbuf.used = (size_t) j; + } else if (!infodump && ch == '\\') { + outbuf.used = (size_t) j; + } else if (ch == delim && (j == 0 || outbuf.text[j - 1] != '\\')) { + outbuf.used = (size_t) (j + 1); + } else { + break; + } + } + outbuf.text[outbuf.used] = '\0'; + } + if (outbuf.text != 0) { + (void) fputs(outbuf.text, stdout); + putchar('\n'); + } + return (int) outbuf.used; +} + +void +compare_entry(PredHook hook, + TERMTYPE *tp GCC_UNUSED, + bool quiet) /* compare two entries */ { - int i, j; - NCURSES_CONST char * name; + PredIdx i, j; + NCURSES_CONST char *name; - (void) fputs(" comparing booleans.\n", stdout); - for_each_boolean(j,tp) - { + if (!quiet) + fputs(" comparing booleans.\n", stdout); + for_each_boolean(j, tp) { i = BoolIndirect(j); - name = ExtBoolname(tp,i,bool_names); + name = ExtBoolname(tp, (int) i, bool_names); - if ((outform == F_LITERAL || outform == F_TERMINFO || outform == F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + if (isObsolete(outform, name)) continue; - (*hook)(BOOLEAN, i, name); + (*hook) (CMP_BOOLEAN, i, name); } - (void) fputs(" comparing numbers.\n", stdout); - for_each_number(j,tp) - { + if (!quiet) + fputs(" comparing numbers.\n", stdout); + for_each_number(j, tp) { i = NumIndirect(j); - name = ExtNumname(tp,i,num_names); + name = ExtNumname(tp, (int) i, num_names); - if ((outform==F_LITERAL || outform==F_TERMINFO || outform==F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + if (isObsolete(outform, name)) continue; - (*hook)(NUMBER, i, name); + (*hook) (CMP_NUMBER, i, name); } - (void) fputs(" comparing strings.\n", stdout); - for_each_string(j,tp) - { + if (!quiet) + fputs(" comparing strings.\n", stdout); + for_each_string(j, tp) { i = StrIndirect(j); - name = ExtStrname(tp,i,str_names); + name = ExtStrname(tp, (int) i, str_names); - if ((outform==F_LITERAL || outform==F_TERMINFO || outform==F_VARIABLE) - && (OBSOLETE(name) && outform != F_LITERAL)) + if (isObsolete(outform, name)) continue; - (*hook)(STRING, i, name); + (*hook) (CMP_STRING, i, name); } + + /* (void) fputs(" comparing use entries.\n", stdout); */ + (*hook) (CMP_USE, 0, "use"); + } #define NOTSET(s) ((s) == 0) @@ -859,7 +1624,8 @@ void compare_entry(void (*hook)(int t, int i, const char *name), TERMTYPE *tp GC #undef CUR #define CUR tp-> -static void set_obsolete_termcaps(TERMTYPE *tp) +static void +set_obsolete_termcaps(TERMTYPE *tp) { #include "capdefaults.c" } @@ -868,46 +1634,47 @@ static void set_obsolete_termcaps(TERMTYPE *tp) * Convert an alternate-character-set string to canonical form: sorted and * unique. */ -static void repair_acsc(TERMTYPE *tp) +void +repair_acsc(TERMTYPE *tp) { - if (VALID_STRING(acs_chars)) { - size_t n, m; - char mapped[256]; - char extra = 0; - unsigned source; - unsigned target; - bool fix_needed = FALSE; - - for (n = 0, source = 0; acs_chars[n] != 0; n++) { - target = acs_chars[n]; - if (source >= target) { - fix_needed = TRUE; - break; - } - source = target; - if (acs_chars[n+1]) - n++; + if (VALID_STRING(acs_chars)) { + size_t n, m; + char mapped[256]; + char extra = 0; + unsigned source; + unsigned target; + bool fix_needed = FALSE; + + for (n = 0, source = 0; acs_chars[n] != 0; n++) { + target = UChar(acs_chars[n]); + if (source >= target) { + fix_needed = TRUE; + break; } - if (fix_needed) { - memset(mapped, 0, sizeof(mapped)); - for (n = 0; acs_chars[n] != 0; n++) { - source = acs_chars[n]; - if ((target = (unsigned char)acs_chars[n+1]) != 0) { - mapped[source] = target; - n++; - } else { - extra = source; - } + source = target; + if (acs_chars[n + 1]) + n++; + } + if (fix_needed) { + memset(mapped, 0, sizeof(mapped)); + for (n = 0; acs_chars[n] != 0; n++) { + source = UChar(acs_chars[n]); + if ((target = (unsigned char) acs_chars[n + 1]) != 0) { + mapped[source] = (char) target; + n++; + } else { + extra = (char) source; } - for (n = m = 0; n < sizeof(mapped); n++) { - if (mapped[n]) { - acs_chars[m++] = n; - acs_chars[m++] = mapped[n]; - } + } + for (n = m = 0; n < sizeof(mapped); n++) { + if (mapped[n]) { + acs_chars[m++] = (char) n; + acs_chars[m++] = mapped[n]; } - if (extra) - acs_chars[m++] = extra; /* garbage in, garbage out */ - acs_chars[m] = 0; } + if (extra) + acs_chars[m++] = extra; /* garbage in, garbage out */ + acs_chars[m] = 0; } + } }