X-Git-Url: http://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Ftinfo%2Fparse_entry.c;h=14bcb67ed6bdff5400438b8697f68966dd9275f5;hp=3fa2f254cb91dd650137efe1f3ed20e690f21adb;hb=HEAD;hpb=b22573b1ba4b51da883fa5f805b52f153fa5fae9 diff --git a/ncurses/tinfo/parse_entry.c b/ncurses/tinfo/parse_entry.c index 3fa2f254..13fef495 100644 --- a/ncurses/tinfo/parse_entry.c +++ b/ncurses/tinfo/parse_entry.c @@ -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 #include -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; } /*