ncurses 6.1 - patch 20190810
[ncurses.git] / ncurses / tinfo / captoinfo.c
index 80ee6a334f7f46fe1af096d1dea8aed0f82b35e1..6a2252ee25406ede681d42f76d2ed7dda5aaba2e 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 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.72 2012/02/22 22:40:24 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 */
 
@@ -114,9 +124,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;
@@ -183,7 +191,7 @@ cvtchar(register const char *sp)
        case '$':
        case '\\':
        case '%':
-           c = (unsigned char) (*sp);
+           c = UChar(*sp);
            len = 2;
            break;
        case '\0':
@@ -196,22 +204,26 @@ 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);
+           c = UChar(*sp);
            len = 2;
            break;
        }
        break;
     case '^':
-       c = (unsigned char) (*++sp & 0x1f);
+       c = UChar(*++sp);
+       if (c == '?')
+           c = 127;
+       else
+           c &= 0x1f;
        len = 2;
        break;
     default:
-       c = (unsigned char) (*sp);
+       c = UChar(*sp);
        len = 1;
     }
     if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
@@ -234,6 +246,8 @@ 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;
@@ -241,7 +255,7 @@ getparm(int parm, int n)
            parm = 1;
     }
 
-    while (n--) {
+    for (nn = 0; nn < n; ++nn) {
        dp = save_string(dp, "%p");
        dp = save_char(dp, '0' + parm);
     }
@@ -250,7 +264,7 @@ getparm(int parm, int n)
        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");
            }
        }
@@ -290,6 +304,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) */
@@ -311,7 +327,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) {
@@ -431,12 +447,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();
@@ -491,6 +512,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);
 }
 
@@ -527,17 +551,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 */
-           _nc_STRCPY(temp, unctrl((chtype) c1), sizeof(temp));
-       else
-           _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(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;
@@ -553,6 +580,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 `%'
@@ -570,6 +599,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().
@@ -585,7 +616,15 @@ _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 params %d, %s", parameterized, str));
 
     /* we may have to move some trailing mandatory padding up front */
     padding = str + strlen(str) - 1;
@@ -602,7 +641,9 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz
            bufptr = save_char(bufptr, *padding++);
     }
 
-    for (; *str && ((trimmed == 0) || (str < trimmed)); str++) {
+    for (; !syntax_error &&
+        *str &&
+        ((trimmed == 0) || (str < trimmed)); str++) {
        int c1, c2;
        char *cp = 0;
 
@@ -610,6 +651,14 @@ _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);
@@ -624,17 +673,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 (_nc_strict_bsd) {
-                   if (isdigit(UChar(xx1))) {
+
+                   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;
 
                        /*
@@ -649,10 +701,31 @@ _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';
                        }
+                       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':
@@ -688,6 +761,24 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz
                            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);
            }
@@ -706,7 +797,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");
@@ -733,8 +825,9 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz
        } 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, "%+");
@@ -777,26 +870,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 (_nc_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 (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')
-                       return 0;
-                   if (ch1 < '2')
+                   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;
 
@@ -814,9 +927,11 @@ _nc_infotocap(const char *cap GCC_UNUSED, const char *str, int const parameteriz
                 * termcap notation.
                 */
            case 's':
-               if (_nc_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':
@@ -828,8 +943,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':
@@ -848,11 +964,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
+               ? "<ERR>"
+               : _nc_visbuf(my_string)));
+
     return (syntax_error ? NULL : my_string);
 }