]> ncurses.scripts.mit.edu Git - ncurses.git/blobdiff - ncurses/tinfo/parse_entry.c
ncurses 6.4 - patch 20240414
[ncurses.git] / ncurses / tinfo / parse_entry.c
index 3fa2f254cb91dd650137efe1f3ed20e690f21adb..13fef495aec9d2aa1ee852d0c71d6af7e622c012 100644 (file)
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2016,2017 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 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            *
@@ -47,7 +48,7 @@
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: parse_entry.c,v 1.86 2017/06/28 00:53:12 tom Exp $")
+MODULE_ID("$Id: parse_entry.c,v 1.108 2023/04/24 22:32:33 tom Exp $")
 
 #ifdef LINT
 static short const parametrized[] =
@@ -63,7 +64,7 @@ static struct name_table_entry const *lookup_fullname(const char *name);
 #if NCURSES_XNAMES
 
 static struct name_table_entry const *
-_nc_extend_names(ENTRY * entryp, char *name, int token_type)
+_nc_extend_names(ENTRY * entryp, const char *name, int token_type)
 {
     static struct name_table_entry temp;
     TERMTYPE2 *tp = &(entryp->tterm);
@@ -109,7 +110,7 @@ _nc_extend_names(ENTRY * entryp, char *name, int token_type)
        /* Well, we are given a cancel for a name that we don't recognize */
        return _nc_extend_names(entryp, name, STRING);
     default:
-       return 0;
+       return NULL;
     }
 
     /* Adjust the 'offset' (insertion-point) to keep the lists of extended
@@ -141,6 +142,11 @@ _nc_extend_names(ENTRY * entryp, char *name, int token_type)
        for (last = (unsigned) (max - 1); last > tindex; last--)
 
     if (!found) {
+       char *saved;
+
+       if ((saved = _nc_save_str(name)) == NULL)
+           return NULL;
+
        switch (token_type) {
        case BOOLEAN:
            tp->ext_Booleans++;
@@ -168,7 +174,7 @@ _nc_extend_names(ENTRY * entryp, char *name, int token_type)
        TYPE_REALLOC(char *, actual, tp->ext_Names);
        while (--actual > offset)
            tp->ext_Names[actual] = tp->ext_Names[actual - 1];
-       tp->ext_Names[offset] = _nc_save_str(name);
+       tp->ext_Names[offset] = saved;
     }
 
     temp.nte_name = tp->ext_Names[offset];
@@ -178,8 +184,78 @@ _nc_extend_names(ENTRY * entryp, char *name, int token_type)
 
     return &temp;
 }
+
+static const char *
+usertype2s(int mask)
+{
+    const char *result = "unknown";
+    if (mask & (1 << BOOLEAN)) {
+       result = "boolean";
+    } else if (mask & (1 << NUMBER)) {
+       result = "number";
+    } else if (mask & (1 << STRING)) {
+       result = "string";
+    }
+    return result;
+}
+
+static bool
+expected_type(const char *name, int token_type, bool silent)
+{
+    struct user_table_entry const *entry = _nc_find_user_entry(name);
+    bool result = TRUE;
+    if ((entry != 0) && (token_type != CANCEL)) {
+       int have_type = (1 << token_type);
+       if (!(entry->ute_type & have_type)) {
+           if (!silent)
+               _nc_warning("expected %s-type for %s, have %s",
+                           usertype2s(entry->ute_type),
+                           name,
+                           usertype2s(have_type));
+           result = FALSE;
+       }
+    }
+    return result;
+}
 #endif /* NCURSES_XNAMES */
 
+/*
+ * A valid entry name uses characters from the "portable character set"
+ * (more commonly referred to as US-ASCII), and disallows some of the
+ * punctuation characters:
+ *
+ * '/' is a pathname separator
+ * '\' may be a pathname separator, but more important, is an escape
+ * '|' delimits names and description
+ * '#' denotes a numeric value
+ * '=' denotes a string value
+ * '@' denotes a cancelled symbol
+ * ',' separates terminfo capabilities
+ * ':' separates termcap capabilities
+ *
+ * Termcap capability names may begin with a '#' or '@' (since they have
+ * exactly two characters).
+ */
+static bool
+valid_entryname(const char *name)
+{
+    bool result = TRUE;
+    bool first = TRUE;
+    int ch;
+    while ((ch = UChar(*name++)) != '\0') {
+       if (ch <= ' ' || ch > '~' || strchr("/\\|=,:", ch) != NULL) {
+           result = FALSE;
+           break;
+       }
+       if (!first && strchr("#@", ch) != NULL) {
+           result = FALSE;
+           break;
+       }
+       first = FALSE;
+    }
+    return result;
+}
+
 /*
  *     int
  *     _nc_parse_entry(entry, literal, silent)
@@ -203,7 +279,7 @@ _nc_extend_names(ENTRY * entryp, char *name, int token_type)
        { bad_tc_usage = TRUE; \
         _nc_warning("Legacy termcap allows only a trailing tc= clause"); }
 
-#define MAX_NUMBER 0x7fff      /* positive shorts only */
+#define MAX_NUMBER MAX_OF_TYPE(NCURSES_INT2)
 
 NCURSES_EXPORT(int)
 _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
@@ -211,12 +287,17 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
     int token_type;
     struct name_table_entry const *entry_ptr;
     char *ptr, *base;
+    const char *name;
     bool bad_tc_usage = FALSE;
 
+    TR(TRACE_DATABASE,
+       (T_CALLED("_nc_parse_entry(entry=%p, literal=%d, silent=%d)"),
+       (void *) entryp, literal, silent));
+
     token_type = _nc_get_token(silent);
 
     if (token_type == EOF)
-       return (EOF);
+       returnDB(EOF);
     if (token_type != NAMES)
        _nc_err_abort("Entry does not start with terminal names in column one");
 
@@ -252,16 +333,21 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
     entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr);
 
     if (entryp->tterm.str_table == 0)
-       return (ERR);
+       returnDB(ERR);
 
-    DEBUG(1, ("Starting '%s'", ptr));
+    DEBUG(2, ("Starting '%s'", ptr));
 
     /*
      * We do this because the one-token lookahead in the parse loop
      * results in the terminal type getting prematurely set to correspond
      * to that of the next entry.
      */
-    _nc_set_type(_nc_first_name(entryp->tterm.term_names));
+    name = _nc_first_name(entryp->tterm.term_names);
+    if (!valid_entryname(name)) {
+       _nc_warning("invalid entry name \"%s\"", name);
+       name = "invalid";
+    }
+    _nc_set_type(name);
 
     /* check for overly-long names and aliases */
     for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0;
@@ -283,9 +369,24 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
        bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0);
        bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0);
        if (is_use || is_tc) {
-           entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring);
-           entryp->uses[entryp->nuses].line = _nc_curr_line;
-           if (VALID_STRING(entryp->uses[entryp->nuses].name)) {
+           char *saved;
+
+           if (!VALID_STRING(_nc_curr_token.tk_valstring)
+               || _nc_curr_token.tk_valstring[0] == '\0') {
+               _nc_warning("missing name for use-clause");
+               continue;
+           } else if (!valid_entryname(_nc_curr_token.tk_valstring)) {
+               _nc_warning("invalid name for use-clause \"%s\"",
+                           _nc_curr_token.tk_valstring);
+               continue;
+           } else if (entryp->nuses >= MAX_USES) {
+               _nc_warning("too many use-clauses, ignored \"%s\"",
+                           _nc_curr_token.tk_valstring);
+               continue;
+           }
+           if ((saved = _nc_save_str(_nc_curr_token.tk_valstring)) != NULL) {
+               entryp->uses[entryp->nuses].name = saved;
+               entryp->uses[entryp->nuses].line = _nc_curr_line;
                entryp->nuses++;
                if (entryp->nuses > 1 && is_tc) {
                    BAD_TC_USAGE
@@ -297,7 +398,7 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
                                       _nc_get_hash_table(_nc_syntax));
 
            /*
-            * Our kluge to handle aliasing.  The reason it's done
+            * Our kluge to handle aliasing.  The reason it is done
             * this ugly way, with a linear search, is so the hashing
             * machinery doesn't have to be made really complicated
             * (also we get better warnings this way).  No point in
@@ -354,12 +455,20 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
             * define a name based on its context.
             */
            if (entry_ptr == NOTFOUND
-               && _nc_user_definable
-               && (entry_ptr = _nc_extend_names(entryp,
-                                                _nc_curr_token.tk_name,
-                                                token_type)) != 0) {
-               if (_nc_tracing >= DEBUG_LEVEL(1))
-                   _nc_warning("extended capability '%s'", _nc_curr_token.tk_name);
+               && _nc_user_definable) {
+               if (expected_type(_nc_curr_token.tk_name, token_type, silent)) {
+                   if ((entry_ptr = _nc_extend_names(entryp,
+                                                     _nc_curr_token.tk_name,
+                                                     token_type)) != 0) {
+                       if (_nc_tracing >= DEBUG_LEVEL(1)) {
+                           _nc_warning("extended capability '%s'",
+                                       _nc_curr_token.tk_name);
+                       }
+                   }
+               } else {
+                   /* ignore it: we have already printed error message */
+                   continue;
+               }
            }
 #endif /* NCURSES_XNAMES */
 
@@ -458,20 +567,27 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
                break;
 
            case NUMBER:
+#if !NCURSES_EXT_NUMBERS
                if (_nc_curr_token.tk_valnumber > MAX_NUMBER) {
                    entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER;
-               } else {
+               } else
+#endif
+               {
                    entryp->tterm.Numbers[entry_ptr->nte_index] =
-                       (short) _nc_curr_token.tk_valnumber;
+                       (NCURSES_INT2) _nc_curr_token.tk_valnumber;
                }
                break;
 
            case STRING:
                ptr = _nc_curr_token.tk_valstring;
-               if (_nc_syntax == SYN_TERMCAP)
+               if (_nc_syntax == SYN_TERMCAP) {
+                   int n = entry_ptr->nte_index;
                    ptr = _nc_captoinfo(_nc_curr_token.tk_name,
                                        ptr,
-                                       parametrized[entry_ptr->nte_index]);
+                                       (n < (int) SIZEOF(parametrized))
+                                       ? parametrized[n]
+                                       : 0);
+               }
                entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
                break;
 
@@ -512,9 +628,11 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
                 * Otherwise, look for a base entry that will already
                 * have picked up defaults via translation.
                 */
-               for (i = 0; i < entryp->nuses; i++)
-                   if (!strchr((char *) entryp->uses[i].name, '+'))
+               for (i = 0; i < entryp->nuses; i++) {
+                   if (entryp->uses[i].name != 0
+                       && !strchr(entryp->uses[i].name, '+'))
                        has_base_entry = TRUE;
+               }
            }
 
            postprocess_termcap(&entryp->tterm, has_base_entry);
@@ -523,7 +641,7 @@ _nc_parse_entry(ENTRY * entryp, int literal, bool silent)
     }
     _nc_wrap_entry(entryp, FALSE);
 
-    return (OK);
+    returnDB(OK);
 }
 
 NCURSES_EXPORT(int)
@@ -577,12 +695,12 @@ _nc_capcmp(const char *s, const char *t)
 }
 
 static void
-append_acs0(string_desc * dst, int code, int src)
+append_acs0(string_desc * dst, int code, char *src, size_t off)
 {
-    if (src != 0) {
+    if (src != 0 && off < strlen(src)) {
        char temp[3];
        temp[0] = (char) code;
-       temp[1] = (char) src;
+       temp[1] = src[off];
        temp[2] = 0;
        _nc_safe_strcat(dst, temp);
     }
@@ -592,7 +710,7 @@ static void
 append_acs(string_desc * dst, int code, char *src)
 {
     if (VALID_STRING(src) && strlen(src) == 1) {
-       append_acs0(dst, code, *src);
+       append_acs0(dst, code, src, 0);
     }
 }
 
@@ -640,13 +758,6 @@ static const char C_LF[] = "\n";
 static const char C_BS[] = "\b";
 static const char C_HT[] = "\t";
 
-/*
- * Note that WANTED and PRESENT are not simple inverses!  If a capability
- * has been explicitly cancelled, it's not considered WANTED.
- */
-#define WANTED(s)      ((s) == ABSENT_STRING)
-#define PRESENT(s)     (((s) != ABSENT_STRING) && ((s) != CANCELLED_STRING))
-
 /*
  * This bit of legerdemain turns all the terminfo variable names into
  * references to locations in the arrays Booleans, Numbers, and Strings ---
@@ -662,6 +773,10 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
     char buf[MAX_LINE * 2 + 2];
     string_desc result;
 
+    TR(TRACE_DATABASE,
+       (T_CALLED("postprocess_termcap(tp=%p, has_base=%d)"),
+       (void *) tp, has_base));
+
     /*
      * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS
      *
@@ -672,10 +787,10 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
 
     /* if there was a tc entry, assume we picked up defaults via that */
     if (!has_base) {
-       if (WANTED(init_3string) && termcap_init2)
+       if (WANTED(init_3string) && PRESENT(termcap_init2))
            init_3string = _nc_save_str(termcap_init2);
 
-       if (WANTED(reset_2string) && termcap_reset)
+       if (WANTED(reset_2string) && PRESENT(termcap_reset))
            reset_2string = _nc_save_str(termcap_reset);
 
        if (WANTED(carriage_return)) {
@@ -790,7 +905,7 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
        if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC)
            _nc_warning("hardware tabs with a width other than 8: %d", init_tabs);
        else {
-           if (tab && _nc_capcmp(tab, C_HT))
+           if (PRESENT(tab) && _nc_capcmp(tab, C_HT))
                _nc_warning("hardware tabs with a non-^I tab string %s",
                            _nc_visbuf(tab));
            else {
@@ -854,7 +969,7 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
            if (tp->Strings[to_ptr->nte_index]) {
                const char *s = tp->Strings[from_ptr->nte_index];
                const char *t = tp->Strings[to_ptr->nte_index];
-               /* There's no point in warning about it if it's the same
+               /* There's no point in warning about it if it is the same
                 * string; that's just an inefficiency.
                 */
                if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 0)
@@ -867,17 +982,22 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
             * The magic moment -- copy the mapped key string over,
             * stripping out padding.
             */
-           for (dp = buf2, bp = tp->Strings[from_ptr->nte_index]; *bp; bp++) {
-               if (bp[0] == '$' && bp[1] == '<') {
-                   while (*bp && *bp != '>') {
-                       ++bp;
-                   }
-               } else
-                   *dp++ = *bp;
-           }
-           *dp = '\0';
+           bp = tp->Strings[from_ptr->nte_index];
+           if (VALID_STRING(bp)) {
+               for (dp = buf2; *bp; bp++) {
+                   if (bp[0] == '$' && bp[1] == '<') {
+                       while (*bp && *bp != '>') {
+                           ++bp;
+                       }
+                   } else
+                       *dp++ = *bp;
+               }
+               *dp = '\0';
 
-           tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
+               tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
+           } else {
+               tp->Strings[to_ptr->nte_index] = bp;
+           }
        }
 
        /*
@@ -886,7 +1006,7 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
         * got mapped to kich1 and im to kIC to avoid a collision.
         * If the description has im but not ic, hack kIC back to kich1.
         */
-       if (foundim && WANTED(key_ic) && key_sic) {
+       if (foundim && WANTED(key_ic) && PRESENT(key_sic)) {
            key_ic = key_sic;
            key_sic = ABSENT_STRING;
        }
@@ -938,16 +1058,21 @@ postprocess_termcap(TERMTYPE2 *tp, bool has_base)
            acs_chars = _nc_save_str(buf2);
            _nc_warning("acsc string synthesized from XENIX capabilities");
        }
-    } else if (acs_chars == 0
-              && enter_alt_charset_mode != 0
-              && exit_alt_charset_mode != 0) {
+    } else if (acs_chars == ABSENT_STRING
+              && PRESENT(enter_alt_charset_mode)
+              && PRESENT(exit_alt_charset_mode)) {
        acs_chars = _nc_save_str(VT_ACSC);
     }
+    returnVoidDB;
 }
 
 static void
 postprocess_terminfo(TERMTYPE2 *tp)
 {
+    TR(TRACE_DATABASE,
+       (T_CALLED("postprocess_terminfo(tp=%p)"),
+       (void *) tp));
+
     /*
      * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION
      * ----------------------------------------------------------------------
@@ -963,17 +1088,17 @@ postprocess_terminfo(TERMTYPE2 *tp)
        _nc_str_init(&result, buf2, sizeof(buf2));
        _nc_safe_strcat(&result, acs_chars);
 
-       append_acs0(&result, 'l', box_chars_1[0]);      /* ACS_ULCORNER */
-       append_acs0(&result, 'q', box_chars_1[1]);      /* ACS_HLINE */
-       append_acs0(&result, 'k', box_chars_1[2]);      /* ACS_URCORNER */
-       append_acs0(&result, 'x', box_chars_1[3]);      /* ACS_VLINE */
-       append_acs0(&result, 'j', box_chars_1[4]);      /* ACS_LRCORNER */
-       append_acs0(&result, 'm', box_chars_1[5]);      /* ACS_LLCORNER */
-       append_acs0(&result, 'w', box_chars_1[6]);      /* ACS_TTEE */
-       append_acs0(&result, 'u', box_chars_1[7]);      /* ACS_RTEE */
-       append_acs0(&result, 'v', box_chars_1[8]);      /* ACS_BTEE */
-       append_acs0(&result, 't', box_chars_1[9]);      /* ACS_LTEE */
-       append_acs0(&result, 'n', box_chars_1[10]);     /* ACS_PLUS */
+       append_acs0(&result, 'l', box_chars_1, 0);      /* ACS_ULCORNER */
+       append_acs0(&result, 'q', box_chars_1, 1);      /* ACS_HLINE */
+       append_acs0(&result, 'k', box_chars_1, 2);      /* ACS_URCORNER */
+       append_acs0(&result, 'x', box_chars_1, 3);      /* ACS_VLINE */
+       append_acs0(&result, 'j', box_chars_1, 4);      /* ACS_LRCORNER */
+       append_acs0(&result, 'm', box_chars_1, 5);      /* ACS_LLCORNER */
+       append_acs0(&result, 'w', box_chars_1, 6);      /* ACS_TTEE */
+       append_acs0(&result, 'u', box_chars_1, 7);      /* ACS_RTEE */
+       append_acs0(&result, 'v', box_chars_1, 8);      /* ACS_BTEE */
+       append_acs0(&result, 't', box_chars_1, 9);      /* ACS_LTEE */
+       append_acs0(&result, 'n', box_chars_1, 10);     /* ACS_PLUS */
 
        if (buf2[0]) {
            acs_chars = _nc_save_str(buf2);
@@ -984,6 +1109,7 @@ postprocess_terminfo(TERMTYPE2 *tp)
     /*
      * ----------------------------------------------------------------------
      */
+    returnVoidDB;
 }
 
 /*