X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Ftinfo%2Fcaptoinfo.c;h=926ac2ff49e953ebb5463db6f5e44a6e3f8ba484;hp=faedb58c8690a8b91920c0302bb2016e68b0d172;hb=312665d3aaaf5d8e3ba34d80fdd650abf758272a;hpb=0c12f3c8ff417105269bda50a3a2fe06c0bfdebf diff --git a/ncurses/tinfo/captoinfo.c b/ncurses/tinfo/captoinfo.c index faedb58c..926ac2ff 100644 --- a/ncurses/tinfo/captoinfo.c +++ b/ncurses/tinfo/captoinfo.c @@ -1,5 +1,6 @@ /**************************************************************************** - * Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. * + * Copyright 2018-2020,2021 Thomas E. Dickey * + * Copyright 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 * @@ -33,12 +34,16 @@ ****************************************************************************/ /* - * captoinfo.c --- conversion between termcap and terminfo formats + * captoinfo.c + * + * Provide conversion in both directions between termcap and terminfo. + * + * cap-to-info --- conversion between termcap and terminfo formats * * The captoinfo() code was swiped from Ross Ridge's mytinfo package, * adapted to fit ncurses by Eric S. Raymond . * - * There is just one entry point: + * It has just one entry point: * * char *_nc_captoinfo(n, s, parameterized) * @@ -93,7 +98,13 @@ #include #include -MODULE_ID("$Id: captoinfo.c,v 1.64 2011/07/23 20:36:28 tom Exp $") +MODULE_ID("$Id: captoinfo.c,v 1.101 2021/02/28 01:05:07 tom Exp $") + +#if 0 +#define DEBUG_THIS(p) DEBUG(9, p) +#else +#define DEBUG_THIS(p) /* nothing */ +#endif #define MAX_PUSHED 16 /* max # args we can push onto the stack */ @@ -114,9 +125,7 @@ init_string(void) /* initialize 'my_string', 'my_length' */ { if (my_string == 0) - my_string = typeMalloc(char, my_length = 256); - if (my_string == 0) - _nc_err_abort(MSG_NO_MEMORY); + TYPE_MALLOC(char, my_length = 256, my_string); *my_string = '\0'; return my_string; @@ -133,7 +142,7 @@ save_string(char *d, const char *const s) _nc_err_abort(MSG_NO_MEMORY); d = my_string + have; } - (void) strcpy(d, s); + _nc_STRCPY(d, s, my_length - have); return d + strlen(d); } @@ -183,7 +192,7 @@ cvtchar(register const char *sp) case '$': case '\\': case '%': - c = (unsigned char) (*sp); + c = UChar(*sp); len = 2; break; case '\0': @@ -196,29 +205,36 @@ cvtchar(register const char *sp) case '3': len = 1; while (isdigit(UChar(*sp))) { - c = (unsigned char) (8 * c + (*sp++ - '0')); + c = UChar(8 * c + (*sp++ - '0')); len++; } break; default: - c = (unsigned char) (*sp); - len = 2; + c = UChar(*sp); + len = (c != '\0') ? 2 : 1; break; } break; case '^': - c = (unsigned char) (*++sp & 0x1f); len = 2; + c = UChar(*++sp); + if (c == '?') { + c = 127; + } else if (c == '\0') { + len = 1; + } else { + c &= 0x1f; + } break; default: - c = (unsigned char) (*sp); - len = 1; + c = UChar(*sp); + len = (c != '\0') ? 1 : 0; } if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') { dp = save_string(dp, "%\'"); dp = save_char(dp, c); dp = save_char(dp, '\''); - } else { + } else if (c != '\0') { dp = save_string(dp, "%{"); if (c > 99) dp = save_char(dp, c / 100 + '0'); @@ -234,17 +250,25 @@ static void getparm(int parm, int n) /* push n copies of param on the terminfo stack if not already there */ { + int nn; + if (seenr) { if (parm == 1) parm = 2; else if (parm == 2) parm = 1; } + + for (nn = 0; nn < n; ++nn) { + dp = save_string(dp, "%p"); + dp = save_char(dp, '0' + parm); + } + if (onstack == parm) { if (n > 1) { _nc_warning("string may not be optimal"); dp = save_string(dp, "%Pa"); - while (n--) { + while (n-- > 0) { dp = save_string(dp, "%ga"); } } @@ -255,11 +279,6 @@ getparm(int parm, int n) onstack = parm; - while (n--) { - dp = save_string(dp, "%p"); - dp = save_char(dp, '0' + parm); - } - if (seenn && parm < 3) { dp = save_string(dp, "%{96}%^"); } @@ -289,6 +308,8 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) seenr = 0; param = 1; + DEBUG_THIS(("_nc_captoinfo params %d, %s", parameterized, s)); + dp = init_string(); /* skip the initial padding (if we haven't been told not to) */ @@ -296,7 +317,7 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) if (s == 0) s = ""; if (parameterized >= 0 && isdigit(UChar(*s))) - for (capstart = s;; s++) + for (capstart = s; *s != '\0'; s++) if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.')) break; @@ -310,7 +331,7 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) } switch (*s++) { case '%': - dp = save_char(dp, '%'); + dp = save_string(dp, "%%"); break; case 'r': if (seenr++ == 1) { @@ -343,13 +364,18 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) dp = save_string(dp, "%{2}%*%-"); break; case '>': - getparm(param, 2); /* %?%{x}%>%t%{y}%+%; */ - dp = save_string(dp, "%?"); - s += cvtchar(s); - dp = save_string(dp, "%>%t"); - s += cvtchar(s); - dp = save_string(dp, "%+%;"); + if (s[0] && s[1]) { + getparm(param, 2); + dp = save_string(dp, "%?"); + s += cvtchar(s); + dp = save_string(dp, "%>%t"); + s += cvtchar(s); + dp = save_string(dp, "%+%;"); + } else { + _nc_warning("expected two characters after %%>"); + dp = save_string(dp, "%>"); + } break; case 'a': if ((*s == '=' || *s == '+' || *s == '-' @@ -430,12 +456,17 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) pop(); break; case '0': /* not clear any of the historical termcaps did this */ - if (*s == '3') + if (*s == '3') { + ++s; goto see03; - else if (*s != '2') - goto invalid; - /* FALLTHRU */ + } + if (*s == '2') { + ++s; + goto see02; + } + goto invalid; case '2': + see02: getparm(param, 1); dp = save_string(dp, "%2d"); pop(); @@ -470,7 +501,8 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) } break; default: - dp = save_char(dp, *s++); + if (*s != '\0') + dp = save_char(dp, *s++); break; } } @@ -481,7 +513,7 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) */ if (capstart) { dp = save_string(dp, "$<"); - for (s = capstart;; s++) + for (s = capstart; *s != '\0'; s++) if (isdigit(UChar(*s)) || *s == '*' || *s == '.') dp = save_char(dp, *s); else @@ -490,6 +522,9 @@ _nc_captoinfo(const char *cap, const char *s, int const parameterized) } (void) save_char(dp, '\0'); + + DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string))); + return (my_string); } @@ -514,7 +549,7 @@ bcd_expression(const char *str) { char buffer[80]; int tst; - sprintf(buffer, fmt, ch1, ch2); + _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) fmt, ch1, ch2); tst = strlen(buffer) - 1; assert(len == tst); } @@ -526,17 +561,20 @@ bcd_expression(const char *str) static char * save_tc_char(char *bufptr, int c1) { - char temp[80]; - if (is7bits(c1) && isprint(c1)) { if (c1 == ':' || c1 == '\\') bufptr = save_char(bufptr, '\\'); bufptr = save_char(bufptr, c1); } else { - if (c1 == (c1 & 0x1f)) /* iscntrl() returns T on 255 */ - (void) strcpy(temp, unctrl((chtype) c1)); - else - (void) sprintf(temp, "\\%03o", c1); + char temp[80]; + + if (c1 == (c1 & 0x1f)) { /* iscntrl() returns T on 255 */ + _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) + "%.20s", unctrl((chtype) c1)); + } else { + _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) + "\\%03o", c1); + } bufptr = save_string(bufptr, temp); } return bufptr; @@ -552,6 +590,8 @@ save_tc_inequality(char *bufptr, int c1, int c2) } /* + * info-to-cap --- conversion between terminfo and termcap formats + * * Here are the capabilities infotocap assumes it can translate to: * * %% output `%' @@ -569,6 +609,8 @@ save_tc_inequality(char *bufptr, int c1, int c2) * %m exclusive-or all parameters with 0177 (not in 4.4BSD) */ +#define octal_fixup(n, c) fixups[n].ch = ((fixups[n].ch << 3) | ((c) - '0')) + /* * Convert a terminfo string to termcap format. Parameters are as in * _nc_captoinfo(). @@ -576,7 +618,6 @@ save_tc_inequality(char *bufptr, int c1, int c2) NCURSES_EXPORT(char *) _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameterized) { - int strict_bsd = 1; /* FIXME - consider making this an option */ int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0; const char *padding; const char *trimmed = 0; @@ -585,12 +626,24 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz char *bufptr = init_string(); char octal[4]; int len; + int digits; bool syntax_error = FALSE; + int myfix = 0; + struct { + int ch; + int offset; + } fixups[MAX_TC_FIXUPS]; + + DEBUG_THIS(("_nc_infotocap %s params %d, %s", + _nc_strict_bsd ? "strict" : "loose", + parameterized, + _nc_visbuf(str))); /* we may have to move some trailing mandatory padding up front */ padding = str + strlen(str) - 1; - if (padding > str && *padding == '>' && *--padding == '/') { - --padding; + if (padding > str && *padding == '>') { + if (padding > (str + 1) && *--padding == '/') + --padding; while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*') padding--; if (padding > str && *padding == '<' && *--padding == '$') @@ -601,7 +654,9 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_char(bufptr, *padding++); } - for (; *str && str != trimmed; str++) { + for (; !syntax_error && + *str && + ((trimmed == 0) || (str < trimmed)); str++) { int c1, c2; char *cp = 0; @@ -609,10 +664,23 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz if (str[1] == '\0' || (str + 1) == trimmed) { bufptr = save_string(bufptr, "\\136"); ++str; + } else if (str[1] == '?') { + /* + * Although the 4.3BSD termcap file has an instance of "kb=^?", + * that appears to be just cut/paste since neither 4.3BSD nor + * 4.4BSD termcap interprets "^?" as DEL. + */ + bufptr = save_string(bufptr, "\\177"); + ++str; } else { bufptr = save_char(bufptr, *str++); bufptr = save_char(bufptr, *str); } + } else if (str[0] == ':') { + bufptr = save_char(bufptr, '\\'); + bufptr = save_char(bufptr, '0'); + bufptr = save_char(bufptr, '7'); + bufptr = save_char(bufptr, '2'); } else if (str[0] == '\\') { if (str[1] == '\0' || (str + 1) == trimmed) { bufptr = save_string(bufptr, "\\134"); @@ -623,17 +691,20 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz } else if (str[1] == ',') { bufptr = save_char(bufptr, *++str); } else { - int xx1, xx2; + int xx1; bufptr = save_char(bufptr, *str++); xx1 = *str; - if (strict_bsd) { - if (isdigit(UChar(xx1))) { + if (_nc_strict_bsd) { + + if (isoctal(UChar(xx1))) { int pad = 0; + int xx2; + int fix = 0; - if (!isdigit(UChar(str[1]))) + if (!isoctal(UChar(str[1]))) pad = 2; - else if (str[1] && !isdigit(UChar(str[2]))) + else if (str[1] && !isoctal(UChar(str[2]))) pad = 1; /* @@ -648,17 +719,36 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz xx2 = '0'; pad = 0; /* FIXME - optionally pad to 3 digits */ } + if (myfix < MAX_TC_FIXUPS) { + fix = 3 - pad; + fixups[myfix].ch = 0; + fixups[myfix].offset = (int) (bufptr + - my_string + - 1); + } while (pad-- > 0) { bufptr = save_char(bufptr, xx2); + if (myfix < MAX_TC_FIXUPS) { + fixups[myfix].ch <<= 3; + fixups[myfix].ch |= (xx2 - '0'); + } xx2 = '0'; } - } else if (strchr("E\\:nrtbf", xx1) == 0) { - /* - * Note: termcap documentation claims that ":" must be - * escaped as "\072", however the documentation is - * incorrect - read the code. - */ + if (myfix < MAX_TC_FIXUPS) { + int n; + for (n = 0; n < fix; ++n) { + fixups[myfix].ch <<= 3; + fixups[myfix].ch |= (str[n] - '0'); + } + if (fixups[myfix].ch < 32) { + ++myfix; + } + } + } else if (strchr("E\\nrtbf", xx1) == 0) { switch (xx1) { + case 'e': + xx1 = 'E'; + break; case 'l': xx1 = 'n'; break; @@ -667,15 +757,46 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_char(bufptr, '4'); xx1 = '0'; break; + case ':': + /* + * Note: termcap documentation claims that ":" + * must be escaped as "\072", however the + * documentation is incorrect - read the code. + * The replacement does not work reliably, + * so the advice is not helpful. + */ + bufptr = save_char(bufptr, '0'); + bufptr = save_char(bufptr, '7'); + xx1 = '2'; + break; default: /* should not happen, but handle this anyway */ - sprintf(octal, "%03o", UChar(xx1)); + _nc_SPRINTF(octal, _nc_SLIMIT(sizeof(octal)) + "%03o", UChar(xx1)); bufptr = save_char(bufptr, octal[0]); bufptr = save_char(bufptr, octal[1]); xx1 = octal[2]; - continue; + break; } } + } else { + if (myfix < MAX_TC_FIXUPS && isoctal(UChar(xx1))) { + bool will_fix = TRUE; + int n; + + fixups[myfix].ch = 0; + fixups[myfix].offset = (int) (bufptr - my_string - 1); + for (n = 0; n < 3; ++n) { + if (isoctal(str[n])) { + octal_fixup(myfix, str[n]); + } else { + will_fix = FALSE; + break; + } + } + if (will_fix && (fixups[myfix].ch < 32)) + ++myfix; + } } bufptr = save_char(bufptr, xx1); } @@ -694,7 +815,8 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz && ((in0 == 4 && in1 == 10 && in2 == 48) || (in0 == 3 && in1 == 9 && in2 == 38))) { /* dumb-down an optimized case from xterm-256color for termcap */ - str = strstr(str, ";m"); + if ((str = strstr(str, ";m")) == 0) + break; /* cannot happen */ ++str; if (in2 == 48) { bufptr = save_string(bufptr, "[48;5;%dm"); @@ -711,18 +833,19 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_tc_inequality(bufptr, c1, c2); } else if (sscanf(str, "%%?%%{%d}%%>%%t%%'%c'%%+%%;", &c1, &ch2) == 2) { str = strchr(str, ';'); - bufptr = save_tc_inequality(bufptr, c1, c2); + bufptr = save_tc_inequality(bufptr, c1, ch2); } else if (sscanf(str, "%%?%%'%c'%%>%%t%%{%d}%%+%%;", &ch1, &c2) == 2) { str = strchr(str, ';'); - bufptr = save_tc_inequality(bufptr, c1, c2); + bufptr = save_tc_inequality(bufptr, ch1, c2); } else if (sscanf(str, "%%?%%'%c'%%>%%t%%'%c'%%+%%;", &ch1, &ch2) == 2) { str = strchr(str, ';'); - bufptr = save_tc_inequality(bufptr, c1, c2); + bufptr = save_tc_inequality(bufptr, ch1, ch2); } else if ((len = bcd_expression(str)) != 0) { str += len; bufptr = save_string(bufptr, "%B"); - } else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1 - || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1) + } else if ((sscanf(str, "%%{%d}%%+%%%c", &c1, &ch2) == 2 + || sscanf(str, "%%'%c'%%+%%%c", &ch1, &ch2) == 2) + && ch2 == 'c' && (cp = strchr(str, '+'))) { str = cp + 2; bufptr = save_string(bufptr, "%+"); @@ -732,15 +855,15 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_tc_char(bufptr, c1); } /* FIXME: this "works" for 'delta' */ - else if (strncmp(str, "%{2}%*%-", 8) == 0) { + else if (strncmp(str, "%{2}%*%-", (size_t) 8) == 0) { str += 7; bufptr = save_string(bufptr, "%D"); - } else if (strncmp(str, "%{96}%^", 7) == 0) { + } else if (strncmp(str, "%{96}%^", (size_t) 7) == 0) { str += 6; if (saw_m++ == 0) { bufptr = save_string(bufptr, "%n"); } - } else if (strncmp(str, "%{127}%^", 8) == 0) { + } else if (strncmp(str, "%{127}%^", (size_t) 8) == 0) { str += 7; if (saw_n++ == 0) { bufptr = save_string(bufptr, "%m"); @@ -765,26 +888,46 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_char(bufptr, '%'); ch1 = 0; ch2 = 0; + digits = 0; while (isdigit(UChar(*str))) { + if (++digits > 2) { + syntax_error = TRUE; + break; + } ch2 = ch1; ch1 = *str++; - if (strict_bsd) { - if (ch1 > '3') - return 0; + if (digits == 2 && ch2 != '0') { + syntax_error = TRUE; + break; + } else if (_nc_strict_bsd) { + if (ch1 > '3') { + syntax_error = TRUE; + break; + } } else { bufptr = save_char(bufptr, ch1); } } - if (strict_bsd) { - if (ch2 != 0 && ch2 != '0') - return 0; - if (ch1 < '2') + if (syntax_error) + break; + /* + * Convert %02 to %2 and %03 to %3 + */ + if (ch2 == '0' && !_nc_strict_bsd) { + ch2 = 0; + bufptr[-2] = bufptr[-1]; + *--bufptr = 0; + } + if (_nc_strict_bsd) { + if (ch2 != 0 && ch2 != '0') { + syntax_error = TRUE; + } else if (ch1 < '2') { ch1 = 'd'; + } bufptr = save_char(bufptr, ch1); } - if (strchr("doxX.", *str)) { - if (*str != 'd') /* termcap doesn't have octal, hex */ - return 0; + if (strchr("oxX.", *str)) { + syntax_error = TRUE; /* termcap doesn't have octal, hex */ } break; @@ -802,9 +945,11 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz * termcap notation. */ case 's': - if (strict_bsd) - return 0; - bufptr = save_string(bufptr, "%s"); + if (_nc_strict_bsd) { + syntax_error = TRUE; + } else { + bufptr = save_string(bufptr, "%s"); + } break; case 'p': @@ -816,8 +961,9 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz bufptr = save_string(bufptr, "%r"); seentwo++; } - } else if (*str >= '3') - return (0); + } else if (*str >= '3') { + syntax_error = TRUE; + } break; case 'i': @@ -836,11 +982,29 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz * but that may not be the end of the string. */ assert(str != 0); - if (*str == '\0') + if (str == 0 || *str == '\0') break; } /* endwhile (*str) */ + if (!syntax_error && + myfix > 0 && + ((int) strlen(my_string) - (4 * myfix)) < MIN_TC_FIXUPS) { + while (--myfix >= 0) { + char *p = fixups[myfix].offset + my_string; + *p++ = '^'; + *p++ = (char) (fixups[myfix].ch | '@'); + while ((p[0] = p[2]) != '\0') { + ++p; + } + } + } + + DEBUG_THIS(("... _nc_infotocap %s", + syntax_error + ? "" + : _nc_visbuf(my_string))); + return (syntax_error ? NULL : my_string); }