📄 parse_entry.c
字号:
/**************************************************************************** * Copyright (c) 1998-2002,2003 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 * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, distribute with modifications, sublicense, and/or sell * * copies of the Software, and to permit persons to whom the Software is * * furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included * * in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * * * Except as contained in this notice, the name(s) of the above copyright * * holders shall not be used in advertising or otherwise to promote the * * sale, use or other dealings in this Software without prior written * * authorization. * ****************************************************************************//**************************************************************************** * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * * and: Eric S. Raymond <esr@snark.thyrsus.com> * * and: Thomas E. Dickey 1996-2003 * ****************************************************************************//* * parse_entry.c -- compile one terminfo or termcap entry * * Get an exact in-core representation of an entry. Don't * try to resolve use or tc capabilities, that is someone * else's job. Depends on the lexical analyzer to get tokens * from the input stream. */#define __INTERNAL_CAPS_VISIBLE#include <curses.priv.h>#include <ctype.h>#include <tic.h>#include <term_entry.h>MODULE_ID("$Id: parse_entry.c,v 1.60 2003/11/08 21:57:09 tom Exp $")#ifdef LINTstatic short const parametrized[] ={0};#else#include <parametrized.h>#endifstatic void postprocess_termcap(TERMTYPE *, bool);static void postprocess_terminfo(TERMTYPE *);static struct name_table_entry const *lookup_fullname(const char *name);#if NCURSES_XNAMESstatic struct name_table_entry const *_nc_extend_names(ENTRY * entryp, char *name, int token_type){ static struct name_table_entry temp; TERMTYPE *tp = &(entryp->tterm); unsigned offset = 0; unsigned actual; unsigned tindex; unsigned first, last, n; bool found; switch (token_type) { case BOOLEAN: first = 0; last = tp->ext_Booleans; offset = tp->ext_Booleans; tindex = tp->num_Booleans; break; case NUMBER: first = tp->ext_Booleans; last = tp->ext_Numbers + first; offset = tp->ext_Booleans + tp->ext_Numbers; tindex = tp->num_Numbers; break; case STRING: first = tp->ext_Booleans + tp->ext_Numbers; last = tp->ext_Strings + first; offset = tp->ext_Booleans + tp->ext_Numbers + tp->ext_Strings; tindex = tp->num_Strings; break; case CANCEL: actual = NUM_EXT_NAMES(tp); for (n = 0; n < actual; n++) { if (!strcmp(name, tp->ext_Names[n])) { if (n > (unsigned) (tp->ext_Booleans + tp->ext_Numbers)) { token_type = STRING; } else if (n > tp->ext_Booleans) { token_type = NUMBER; } else { token_type = BOOLEAN; } return _nc_extend_names(entryp, name, 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; } /* Adjust the 'offset' (insertion-point) to keep the lists of extended * names sorted. */ for (n = first, found = FALSE; n < last; n++) { int cmp = strcmp(tp->ext_Names[n], name); if (cmp == 0) found = TRUE; if (cmp >= 0) { offset = n; tindex = n - first; switch (token_type) { case BOOLEAN: tindex += BOOLCOUNT; break; case NUMBER: tindex += NUMCOUNT; break; case STRING: tindex += STRCOUNT; break; } break; } } if (!found) { switch (token_type) { case BOOLEAN: tp->ext_Booleans += 1; tp->num_Booleans += 1; tp->Booleans = typeRealloc(char, tp->num_Booleans, tp->Booleans); for (last = tp->num_Booleans - 1; last > tindex; last--) tp->Booleans[last] = tp->Booleans[last - 1]; break; case NUMBER: tp->ext_Numbers += 1; tp->num_Numbers += 1; tp->Numbers = typeRealloc(short, tp->num_Numbers, tp->Numbers); for (last = tp->num_Numbers - 1; last > tindex; last--) tp->Numbers[last] = tp->Numbers[last - 1]; break; case STRING: tp->ext_Strings += 1; tp->num_Strings += 1; tp->Strings = typeRealloc(char *, tp->num_Strings, tp->Strings); for (last = tp->num_Strings - 1; last > tindex; last--) tp->Strings[last] = tp->Strings[last - 1]; break; } actual = NUM_EXT_NAMES(tp); tp->ext_Names = typeRealloc(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); } temp.nte_name = tp->ext_Names[offset]; temp.nte_type = token_type; temp.nte_index = tindex; temp.nte_link = -1; return &temp;}#endif /* NCURSES_XNAMES *//* * int * _nc_parse_entry(entry, literal, silent) * * Compile one entry. Doesn't try to resolve use or tc capabilities. * * found-forward-use = FALSE * re-initialise internal arrays * get_token(); * if the token was not a name in column 1, complain and die * save names in entry's string table * while (get_token() is not EOF and not NAMES) * check for existence and type-correctness * enter cap into structure * if STRING * save string in entry's string table * push back token */#define BAD_TC_USAGE if (!bad_tc_usage) \ { bad_tc_usage = TRUE; \ _nc_warning("Legacy termcap allows only a trailing tc= clause"); }NCURSES_EXPORT(int)_nc_parse_entry(struct entry *entryp, int literal, bool silent){ int token_type; struct name_table_entry const *entry_ptr; char *ptr, *base; bool bad_tc_usage = FALSE; token_type = _nc_get_token(silent); if (token_type == EOF) return (EOF); if (token_type != NAMES) _nc_err_abort("Entry does not start with terminal names in column one"); _nc_init_entry(&entryp->tterm); entryp->cstart = _nc_comment_start; entryp->cend = _nc_comment_end; entryp->startline = _nc_start_line; DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend)); /* * Strip off the 2-character termcap name, if present. Originally termcap * used that as an indexing aid. We can retain 2-character terminfo names, * but note that they would be lost if we translate to/from termcap. This * feature is supposedly obsolete since "newer" BSD implementations do not * use it; however our reference for this feature is SunOS 4.x, which * implemented it. Note that the resulting terminal type was never the * 2-character name, but was instead the first alias after that. */ ptr = _nc_curr_token.tk_name; if (_nc_syntax == SYN_TERMCAP) { if (ptr[2] == '|') { ptr += 3; _nc_curr_token.tk_name[2] = '\0'; } } entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr); DEBUG(1, ("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)); /* check for overly-long names and aliases */ for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0; base = ptr + 1) { if (ptr - base > MAX_ALIAS) { _nc_warning("%s `%.*s' may be too long", (base == entryp->tterm.term_names) ? "primary name" : "alias", (int) (ptr - base), base); } } entryp->nuses = 0; for (token_type = _nc_get_token(silent); token_type != EOF && token_type != NAMES; token_type = _nc_get_token(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; entryp->nuses++; if (entryp->nuses > 1 && is_tc) { BAD_TC_USAGE } } else { /* normal token lookup */ entry_ptr = _nc_find_entry(_nc_curr_token.tk_name, _nc_syntax ? _nc_cap_hash_table : _nc_info_hash_table); /* * Our kluge to handle aliasing. The reason it's 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 * making this case fast, aliased caps aren't common now * and will get rarer. */ if (entry_ptr == NOTFOUND) { const struct alias *ap; if (_nc_syntax == SYN_TERMCAP) { if (entryp->nuses != 0) { BAD_TC_USAGE } for (ap = _nc_capalias_table; ap->from; ap++) if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { if (ap->to == (char *) 0) { _nc_warning("%s (%s termcap extension) ignored", ap->from, ap->source); goto nexttok; } entry_ptr = _nc_find_entry(ap->to, _nc_cap_hash_table); if (entry_ptr && !silent) _nc_warning("%s (%s termcap extension) aliased to %s", ap->from, ap->source, ap->to); break; } } else { /* if (_nc_syntax == SYN_TERMINFO) */ for (ap = _nc_infoalias_table; ap->from; ap++) if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { if (ap->to == (char *) 0) { _nc_warning("%s (%s terminfo extension) ignored", ap->from, ap->source); goto nexttok; } entry_ptr = _nc_find_entry(ap->to, _nc_info_hash_table); if (entry_ptr && !silent) _nc_warning("%s (%s terminfo extension) aliased to %s", ap->from, ap->source, ap->to); break; } if (entry_ptr == NOTFOUND) { entry_ptr = lookup_fullname(_nc_curr_token.tk_name); } } }#if NCURSES_XNAMES /* * If we have extended-names active, we will automatically * 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); }#endif /* NCURSES_XNAMES */ /* can't find this cap name, not even as an alias */ if (entry_ptr == NOTFOUND) { if (!silent) _nc_warning("unknown capability '%s'", _nc_curr_token.tk_name); continue; } /* deal with bad type/value combinations. */ if (token_type != CANCEL && entry_ptr->nte_type != token_type) { /* * Nasty special cases here handle situations in which type * information can resolve name clashes. Normal lookup * finds the last instance in the capability table of a * given name, regardless of type. find_type_entry looks * for a first matching instance with given type. So as * long as all ambiguous names occur in pairs of distinct * type, this will do the job. */ /* tell max_attributes from arrow_key_map */ if (token_type == NUMBER && !strcmp("ma", _nc_curr_token.tk_name)) entry_ptr = _nc_find_type_entry("ma", NUMBER, _nc_get_table(_nc_syntax != 0)); /* map terminfo's string MT to MT */ else if (token_type == STRING && !strcmp("MT", _nc_curr_token.tk_name)) entry_ptr = _nc_find_type_entry("MT", STRING, _nc_get_table(_nc_syntax != 0)); /* treat strings without following "=" as empty strings */ else if (token_type == BOOLEAN && entry_ptr->nte_type == STRING) token_type = STRING; /* we couldn't recover; skip this token */ else { if (!silent) { const char *type_name; switch (entry_ptr->nte_type) { case BOOLEAN: type_name = "boolean"; break; case STRING: type_name = "string"; break; case NUMBER: type_name = "numeric"; break; default: type_name = "unknown"; break; } _nc_warning("wrong type used for %s capability '%s'", type_name, _nc_curr_token.tk_name); } continue; } } /* now we know that the type/value combination is OK */ switch (token_type) { case CANCEL: switch (entry_ptr->nte_type) { case BOOLEAN: entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN; break; case NUMBER: entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC; break; case STRING: entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING; break; } break; case BOOLEAN: entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE; break; case NUMBER: entryp->tterm.Numbers[entry_ptr->nte_index] = _nc_curr_token.tk_valnumber; break; case STRING: ptr = _nc_curr_token.tk_valstring; if (_nc_syntax == SYN_TERMCAP) ptr = _nc_captoinfo(_nc_curr_token.tk_name, ptr, parametrized[entry_ptr->nte_index]); entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr); break; default: if (!silent) _nc_warning("unknown token type"); _nc_panic_mode((_nc_syntax == SYN_TERMCAP) ? ':' : ','); continue; } } /* end else cur_token.name != "use" */ nexttok: continue; /* cannot have a label w/o statement */ } /* endwhile (not EOF and not NAMES) */ _nc_push_token(token_type); _nc_set_type(_nc_first_name(entryp->tterm.term_names)); /* * Try to deduce as much as possible from extension capabilities * (this includes obsolete BSD capabilities). Sigh...it would be more * space-efficient to call this after use resolution, but it has * to be done before entry allocation is wrapped up. */ if (!literal) { if (_nc_syntax == SYN_TERMCAP) { bool has_base_entry = FALSE; int i; /* * Don't insert defaults if this is a `+' entry meant only * for inclusion in other entries (not sure termcap ever * had these, actually). */ if (strchr(entryp->tterm.term_names, '+')) has_base_entry = TRUE; else /* * 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, '+')) has_base_entry = TRUE; postprocess_termcap(&entryp->tterm, has_base_entry); } else postprocess_terminfo(&entryp->tterm); } _nc_wrap_entry(entryp, FALSE); return (OK);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -