/****************************************************************************
- * Copyright (c) 1998-2006,2008 Free Software Foundation, Inc. *
+ * Copyright (c) 1998-2017,2018 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.51 2008/08/03 18:35:47 tom Exp $")
+MODULE_ID("$Id: captoinfo.c,v 1.96 2018/05/12 16:46:55 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;
static char *
save_string(char *d, const char *const s)
{
- size_t have = (d - my_string);
+ size_t have = (size_t) (d - my_string);
size_t need = have + strlen(s) + 2;
if (need > my_length) {
- my_string = (char *) realloc(my_string, my_length = (need + need));
+ my_string = (char *) _nc_doalloc(my_string, my_length = (need + need));
if (my_string == 0)
_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 = *sp;
+ c = UChar(*sp);
len = 2;
break;
case '\0':
case '3':
len = 1;
while (isdigit(UChar(*sp))) {
- c = 8 * c + (*sp++ - '0');
+ c = UChar(8 * c + (*sp++ - '0'));
len++;
}
break;
default:
- c = *sp;
+ c = UChar(*sp);
len = 2;
break;
}
break;
case '^':
- c = (*++sp & 0x1f);
+ c = UChar(*++sp);
+ if (c == '?')
+ c = 127;
+ else
+ c &= 0x1f;
len = 2;
break;
default:
- c = *sp;
+ c = UChar(*sp);
len = 1;
}
if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
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) */
}
switch (*s++) {
case '%':
- dp = save_char(dp, '%');
+ dp = save_string(dp, "%%");
break;
case 'r':
if (seenr++ == 1) {
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;
}
break;
-#ifdef REVISIBILIZE
- case '\\':
- dp = save_char(dp, *s++);
- dp = save_char(dp, *s++);
- break;
- case '\n':
- dp = save_string(dp, "\\n");
- s++;
- break;
- case '\t':
- dp = save_string(dp, "\\t");
- s++;
- break;
- case '\r':
- dp = save_string(dp, "\\r");
- s++;
- break;
- case '\200':
- dp = save_string(dp, "\\0");
- s++;
- break;
- case '\f':
- dp = save_string(dp, "\\f");
- s++;
- break;
- case '\b':
- dp = save_string(dp, "\\b");
- s++;
- break;
- case ' ':
- dp = save_string(dp, "\\s");
- s++;
- break;
- case '^':
- dp = save_string(dp, "\\^");
- s++;
- break;
- case ':':
- dp = save_string(dp, "\\:");
- s++;
- break;
- case ',':
- dp = save_string(dp, "\\,");
- s++;
- break;
- default:
- if (*s == '\033') {
- dp = save_string(dp, "\\E");
- s++;
- } else if (*s > 0 && *s < 32) {
- dp = save_char(dp, '^');
- dp = save_char(dp, *s + '@');
- s++;
- } else if (*s <= 0 || *s >= 127) {
- dp = save_char(dp, '\\');
- dp = save_char(dp, ((*s & 0300) >> 6) + '0');
- dp = save_char(dp, ((*s & 0070) >> 3) + '0');
- dp = save_char(dp, (*s & 0007) + '0');
- s++;
- } else
- dp = save_char(dp, *s++);
- break;
-#else
default:
dp = save_char(dp, *s++);
break;
-#endif
}
}
}
(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().
int seenone = 0, seentwo = 0, saw_m = 0, saw_n = 0;
const char *padding;
const char *trimmed = 0;
+ int in0, in1, in2;
char ch1 = 0, ch2 = 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 == '/')
+ --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[0] == '\\' && (str[1] == '^' || str[1] == ',')) {
- bufptr = save_char(bufptr, *++str);
+ if (str[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[0] == '\\') {
+ if (str[1] == '\0' || (str + 1) == trimmed) {
+ bufptr = save_string(bufptr, "\\134");
+ ++str;
+ } else if (str[1] == '^') {
+ bufptr = save_string(bufptr, "\\136");
+ ++str;
+ } else if (str[1] == ',') {
+ bufptr = save_char(bufptr, *++str);
+ } else {
+ int xx1;
+
+ bufptr = save_char(bufptr, *str++);
+ xx1 = *str;
+ if (_nc_strict_bsd) {
+
+ if (isoctal(UChar(xx1))) {
+ int pad = 0;
+ int xx2;
+ int fix = 0;
+
+ if (!isoctal(UChar(str[1])))
+ pad = 2;
+ else if (str[1] && !isoctal(UChar(str[2])))
+ pad = 1;
+
+ /*
+ * Test for "\0", "\00" or "\000" and transform those
+ * into "\200".
+ */
+ if (xx1 == '0'
+ && ((pad == 2) || (str[1] == '0'))
+ && ((pad >= 1) || (str[2] == '0'))) {
+ xx2 = '2';
+ } else {
+ 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';
+ }
+ 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;
+ case 's':
+ bufptr = save_char(bufptr, '0');
+ 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 */
+ _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];
+ 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);
+ }
} else if (str[0] == '$' && str[1] == '<') { /* discard padding */
str += 2;
while (isdigit(UChar(*str))
|| *str == '>')
str++;
--str;
+ } else if (sscanf(str,
+ "[%%?%%p1%%{8}%%<%%t%d%%p1%%d%%e%%p1%%{16}%%<%%t%d%%p1%%{8}%%-%%d%%e%d;5;%%p1%%d%%;m",
+ &in0, &in1, &in2) == 3
+ && ((in0 == 4 && in1 == 10 && in2 == 48)
+ || (in0 == 3 && in1 == 9 && in2 == 38))) {
+ /* dumb-down an optimized case from xterm-256color for termcap */
+ if ((str = strstr(str, ";m")) == 0)
+ break; /* cannot happen */
+ ++str;
+ if (in2 == 48) {
+ bufptr = save_string(bufptr, "[48;5;%dm");
+ } else {
+ bufptr = save_string(bufptr, "[38;5;%dm");
+ }
} else if (str[0] == '%' && str[1] == '%') { /* escaped '%' */
bufptr = save_string(bufptr, "%%");
++str;
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");
case '8':
case '9':
bufptr = save_char(bufptr, '%');
- while (isdigit(UChar(*str)))
- bufptr = save_char(bufptr, *str++);
- if (strchr("doxX.", *str)) {
- if (*str != 'd') /* termcap doesn't have octal, hex */
- return 0;
+ ch1 = 0;
+ ch2 = 0;
+ digits = 0;
+ while (isdigit(UChar(*str))) {
+ if (++digits > 2) {
+ syntax_error = TRUE;
+ break;
+ }
+ ch2 = ch1;
+ ch1 = *str++;
+ 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 (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("oxX.", *str)) {
+ syntax_error = TRUE; /* termcap doesn't have octal, hex */
}
break;
* termcap notation.
*/
case 's':
- 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);
}