/****************************************************************************
- * Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. *
+ * Copyright 2018-2019,2020 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 *
****************************************************************************/
/*
- * 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 <esr@snark.thyrsus.com>.
*
- * There is just one entry point:
+ * It has just one entry point:
*
* char *_nc_captoinfo(n, s, parameterized)
*
#include <ctype.h>
#include <tic.h>
-MODULE_ID("$Id: captoinfo.c,v 1.64 2011/07/23 20:36:28 tom Exp $")
+MODULE_ID("$Id: captoinfo.c,v 1.100 2020/07/08 21:39:54 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 */
/* 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;
_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);
}
case '$':
case '\\':
case '%':
- c = (unsigned char) (*sp);
+ c = UChar(*sp);
len = 2;
break;
case '\0':
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');
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");
}
}
onstack = parm;
- while (n--) {
- dp = save_string(dp, "%p");
- dp = save_char(dp, '0' + parm);
- }
-
if (seenn && parm < 3) {
dp = save_string(dp, "%{96}%^");
}
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) */
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;
}
switch (*s++) {
case '%':
- dp = save_char(dp, '%');
+ dp = save_string(dp, "%%");
break;
case 'r':
if (seenr++ == 1) {
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 == '-'
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();
}
break;
default:
- dp = save_char(dp, *s++);
+ if (*s != '\0')
+ dp = save_char(dp, *s++);
break;
}
}
*/
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
}
(void) save_char(dp, '\0');
+
+ DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string)));
+
return (my_string);
}
{
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);
}
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;
}
/*
+ * info-to-cap --- conversion between terminfo and termcap formats
+ *
* Here are the capabilities infotocap assumes it can translate to:
*
* %% output `%'
* %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().
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;
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 params %d, %s", parameterized, 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 == '$')
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;
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[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;
/*
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;
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);
}
&& ((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");
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, "%+");
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");
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;
* 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':
bufptr = save_string(bufptr, "%r");
seentwo++;
}
- } else if (*str >= '3')
- return (0);
+ } else if (*str >= '3') {
+ syntax_error = TRUE;
+ }
break;
case 'i':
* 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
+ ? "<ERR>"
+ : _nc_visbuf(my_string)));
+
return (syntax_error ? NULL : my_string);
}