| /**************************************************************************** |
| * Copyright (c) 1998-2007,2008 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-on * |
| ****************************************************************************/ |
| |
| /* |
| * 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.69 2008/08/16 21:52:03 tom Exp $") |
| |
| #ifdef LINT |
| static short const parametrized[] = |
| {0}; |
| #else |
| #include <parametrized.h> |
| #endif |
| |
| static void postprocess_termcap(TERMTYPE *, bool); |
| static void postprocess_terminfo(TERMTYPE *); |
| 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) |
| { |
| 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(NCURSES_SBOOL, 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 NCURSES_XNAMES |
| && !_nc_user_definable |
| #endif |
| ) { |
| if (ptr[2] == '|') { |
| ptr += 3; |
| _nc_curr_token.tk_name[2] = '\0'; |
| } |
| } |
| |
| entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr); |
| |
| if (entryp->tterm.str_table == 0) |
| return (ERR); |
| |
| 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_get_hash_table(_nc_syntax)); |
| |
| /* |
| * 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_get_alias_table(TRUE); 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_get_hash_table(TRUE)); |
| 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_get_alias_table(FALSE); 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_get_hash_table(FALSE)); |
| 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. |
| */ |
| |
| if (token_type == NUMBER |
| && !strcmp("ma", _nc_curr_token.tk_name)) { |
| /* tell max_attributes from arrow_key_map */ |
| entry_ptr = _nc_find_type_entry("ma", NUMBER, |
| _nc_get_table(_nc_syntax |
| != 0)); |
| assert(entry_ptr != 0); |
| |
| } else if (token_type == STRING |
| && !strcmp("MT", _nc_curr_token.tk_name)) { |
| /* map terminfo's string MT to MT */ |
| entry_ptr = _nc_find_type_entry("MT", STRING, |
| _nc_get_table(_nc_syntax |
| != 0)); |
| assert(entry_ptr != 0); |
| |
| } else if (token_type == BOOLEAN |
| && entry_ptr->nte_type == STRING) { |
| /* treat strings without following "=" as empty strings */ |
| token_type = STRING; |
| } else { |
| /* we couldn't recover; skip this token */ |
| 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((char) ((_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; |
| unsigned 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); |
| } |
| |
| NCURSES_EXPORT(int) |
| _nc_capcmp(const char *s, const char *t) |
| /* compare two string capabilities, stripping out padding */ |
| { |
| if (!s && !t) |
| return (0); |
| else if (!s || !t) |
| return (1); |
| |
| for (;;) { |
| if (s[0] == '$' && s[1] == '<') { |
| for (s += 2;; s++) |
| if (!(isdigit(UChar(*s)) |
| || *s == '.' |
| || *s == '*' |
| || *s == '/' |
| || *s == '>')) |
| break; |
| } |
| |
| if (t[0] == '$' && t[1] == '<') { |
| for (t += 2;; t++) |
| if (!(isdigit(UChar(*t)) |
| || *t == '.' |
| || *t == '*' |
| || *t == '/' |
| || *t == '>')) |
| break; |
| } |
| |
| /* we've now pushed s and t past any padding they were pointing at */ |
| |
| if (*s == '\0' && *t == '\0') |
| return (0); |
| |
| if (*s != *t) |
| return (*t - *s); |
| |
| /* else *s == *t but one is not NUL, so continue */ |
| s++, t++; |
| } |
| } |
| |
| static void |
| append_acs0(string_desc * dst, int code, int src) |
| { |
| if (src != 0) { |
| char temp[3]; |
| temp[0] = (char) code; |
| temp[1] = (char) src; |
| temp[2] = 0; |
| _nc_safe_strcat(dst, temp); |
| } |
| } |
| |
| static void |
| append_acs(string_desc * dst, int code, char *src) |
| { |
| if (src != 0 && strlen(src) == 1) { |
| append_acs0(dst, code, *src); |
| } |
| } |
| |
| /* |
| * The ko capability, if present, consists of a comma-separated capability |
| * list. For each capability, we may assume there is a keycap that sends the |
| * string which is the value of that capability. |
| */ |
| typedef struct { |
| const char *from; |
| const char *to; |
| } assoc; |
| static assoc const ko_xlate[] = |
| { |
| {"al", "kil1"}, /* insert line key -> KEY_IL */ |
| {"bt", "kcbt"}, /* back tab -> KEY_BTAB */ |
| {"cd", "ked"}, /* clear-to-eos key -> KEY_EOL */ |
| {"ce", "kel"}, /* clear-to-eol key -> KEY_EOS */ |
| {"cl", "kclr"}, /* clear key -> KEY_CLEAR */ |
| {"ct", "tbc"}, /* clear all tabs -> KEY_CATAB */ |
| {"dc", "kdch1"}, /* delete char -> KEY_DC */ |
| {"dl", "kdl1"}, /* delete line -> KEY_DL */ |
| {"do", "kcud1"}, /* down key -> KEY_DOWN */ |
| {"ei", "krmir"}, /* exit insert key -> KEY_EIC */ |
| {"ho", "khome"}, /* home key -> KEY_HOME */ |
| {"ic", "kich1"}, /* insert char key -> KEY_IC */ |
| {"im", "kIC"}, /* insert-mode key -> KEY_SIC */ |
| {"le", "kcub1"}, /* le key -> KEY_LEFT */ |
| {"nd", "kcuf1"}, /* nd key -> KEY_RIGHT */ |
| {"nl", "kent"}, /* new line key -> KEY_ENTER */ |
| {"st", "khts"}, /* set-tab key -> KEY_STAB */ |
| {"ta", CANCELLED_STRING}, |
| {"up", "kcuu1"}, /* up-arrow key -> KEY_UP */ |
| {(char *) 0, (char *) 0}, |
| }; |
| |
| /* |
| * This routine fills in string caps that either had defaults under |
| * termcap or can be manufactured from obsolete termcap capabilities. |
| * It was lifted from Ross Ridge's mytinfo package. |
| */ |
| |
| static const char C_CR[] = "\r"; |
| 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 --- |
| * precisely what's needed. |
| */ |
| |
| #undef CUR |
| #define CUR tp-> |
| |
| static void |
| postprocess_termcap(TERMTYPE *tp, bool has_base) |
| { |
| char buf[MAX_LINE * 2 + 2]; |
| string_desc result; |
| |
| /* |
| * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS |
| * |
| * This first part of the code is the functional inverse of the |
| * fragment in capdefaults.c. |
| * ---------------------------------------------------------------------- |
| */ |
| |
| /* if there was a tc entry, assume we picked up defaults via that */ |
| if (!has_base) { |
| if (WANTED(init_3string) && termcap_init2) |
| init_3string = _nc_save_str(termcap_init2); |
| |
| if (WANTED(reset_2string) && termcap_reset) |
| reset_2string = _nc_save_str(termcap_reset); |
| |
| if (WANTED(carriage_return)) { |
| if (carriage_return_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_CR, carriage_return_delay); |
| carriage_return = _nc_save_str(buf); |
| } else |
| carriage_return = _nc_save_str(C_CR); |
| } |
| if (WANTED(cursor_left)) { |
| if (backspace_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_BS, backspace_delay); |
| cursor_left = _nc_save_str(buf); |
| } else if (backspaces_with_bs == 1) |
| cursor_left = _nc_save_str(C_BS); |
| else if (PRESENT(backspace_if_not_bs)) |
| cursor_left = backspace_if_not_bs; |
| } |
| /* vi doesn't use "do", but it does seems to use nl (or '\n') instead */ |
| if (WANTED(cursor_down)) { |
| if (PRESENT(linefeed_if_not_lf)) |
| cursor_down = linefeed_if_not_lf; |
| else if (linefeed_is_newline != 1) { |
| if (new_line_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_LF, new_line_delay); |
| cursor_down = _nc_save_str(buf); |
| } else |
| cursor_down = _nc_save_str(C_LF); |
| } |
| } |
| if (WANTED(scroll_forward) && crt_no_scrolling != 1) { |
| if (PRESENT(linefeed_if_not_lf)) |
| cursor_down = linefeed_if_not_lf; |
| else if (linefeed_is_newline != 1) { |
| if (new_line_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_LF, new_line_delay); |
| scroll_forward = _nc_save_str(buf); |
| } else |
| scroll_forward = _nc_save_str(C_LF); |
| } |
| } |
| if (WANTED(newline)) { |
| if (linefeed_is_newline == 1) { |
| if (new_line_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_LF, new_line_delay); |
| newline = _nc_save_str(buf); |
| } else |
| newline = _nc_save_str(C_LF); |
| } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) { |
| _nc_str_init(&result, buf, sizeof(buf)); |
| if (_nc_safe_strcat(&result, carriage_return) |
| && _nc_safe_strcat(&result, scroll_forward)) |
| newline = _nc_save_str(buf); |
| } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) { |
| _nc_str_init(&result, buf, sizeof(buf)); |
| if (_nc_safe_strcat(&result, carriage_return) |
| && _nc_safe_strcat(&result, cursor_down)) |
| newline = _nc_save_str(buf); |
| } |
| } |
| } |
| |
| /* |
| * Inverse of capdefaults.c code ends here. |
| * ---------------------------------------------------------------------- |
| * |
| * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION |
| * |
| * These translations will *not* be inverted by tgetent(). |
| */ |
| |
| if (!has_base) { |
| /* |
| * We wait until now to decide if we've got a working cr because even |
| * one that doesn't work can be used for newline. Unfortunately the |
| * space allocated for it is wasted. |
| */ |
| if (return_does_clr_eol == 1 || no_correctly_working_cr == 1) |
| carriage_return = ABSENT_STRING; |
| |
| /* |
| * Supposedly most termcap entries have ta now and '\t' is no longer a |
| * default, but it doesn't seem to be true... |
| */ |
| if (WANTED(tab)) { |
| if (horizontal_tab_delay > 0) { |
| sprintf(buf, "%s$<%d>", C_HT, horizontal_tab_delay); |
| tab = _nc_save_str(buf); |
| } else |
| tab = _nc_save_str(C_HT); |
| } |
| if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE) |
| init_tabs = 8; |
| |
| /* |
| * Assume we can beep with ^G unless we're given bl@. |
| */ |
| if (WANTED(bell)) |
| bell = _nc_save_str("\007"); |
| } |
| |
| /* |
| * Translate the old termcap :pt: capability to it#8 + ht=\t |
| */ |
| if (has_hardware_tabs == TRUE) { |
| 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)) |
| _nc_warning("hardware tabs with a non-^I tab string %s", |
| _nc_visbuf(tab)); |
| else { |
| if (WANTED(tab)) |
| tab = _nc_save_str(C_HT); |
| init_tabs = 8; |
| } |
| } |
| } |
| /* |
| * Now translate the ko capability, if there is one. This |
| * isn't from mytinfo... |
| */ |
| if (PRESENT(other_non_function_keys)) { |
| char *base = other_non_function_keys; |
| char *bp, *cp, *dp; |
| struct name_table_entry const *from_ptr; |
| struct name_table_entry const *to_ptr; |
| assoc const *ap; |
| char buf2[MAX_TERMINFO_LENGTH]; |
| bool foundim; |
| |
| /* we're going to use this for a special case later */ |
| dp = strchr(other_non_function_keys, 'i'); |
| foundim = (dp != 0) && (dp[1] == 'm'); |
| |
| /* look at each comma-separated capability in the ko string... */ |
| for (base = other_non_function_keys; |
| (cp = strchr(base, ',')) != 0; |
| base = cp + 1) { |
| size_t len = cp - base; |
| |
| for (ap = ko_xlate; ap->from; ap++) { |
| if (len == strlen(ap->from) |
| && strncmp(ap->from, base, len) == 0) |
| break; |
| } |
| if (!(ap->from && ap->to)) { |
| _nc_warning("unknown capability `%.*s' in ko string", |
| (int) len, base); |
| continue; |
| } else if (ap->to == CANCELLED_STRING) /* ignore it */ |
| continue; |
| |
| /* now we know we found a match in ko_table, so... */ |
| |
| from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE)); |
| to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE)); |
| |
| if (!from_ptr || !to_ptr) /* should never happen! */ |
| _nc_err_abort("ko translation table is invalid, I give up"); |
| |
| if (WANTED(tp->Strings[from_ptr->nte_index])) { |
| _nc_warning("no value for ko capability %s", ap->from); |
| continue; |
| } |
| |
| if (tp->Strings[to_ptr->nte_index]) { |
| /* There's no point in warning about it if it's the same |
| * string; that's just an inefficiency. |
| */ |
| if (strcmp( |
| tp->Strings[from_ptr->nte_index], |
| tp->Strings[to_ptr->nte_index]) != 0) |
| _nc_warning("%s (%s) already has an explicit value %s, ignoring ko", |
| ap->to, ap->from, |
| _nc_visbuf(tp->Strings[to_ptr->nte_index])); |
| continue; |
| } |
| |
| /* |
| * 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'; |
| |
| tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2); |
| } |
| |
| /* |
| * Note: ko=im and ko=ic both want to grab the `Insert' |
| * keycap. There's a kich1 but no ksmir, so the ic capability |
| * 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) { |
| key_ic = key_sic; |
| key_sic = ABSENT_STRING; |
| } |
| } |
| |
| if (!has_base) { |
| if (!hard_copy) { |
| if (WANTED(key_backspace)) |
| key_backspace = _nc_save_str(C_BS); |
| if (WANTED(key_left)) |
| key_left = _nc_save_str(C_BS); |
| if (WANTED(key_down)) |
| key_down = _nc_save_str(C_LF); |
| } |
| } |
| |
| /* |
| * Translate XENIX forms characters. |
| */ |
| if (PRESENT(acs_ulcorner) || |
| PRESENT(acs_llcorner) || |
| PRESENT(acs_urcorner) || |
| PRESENT(acs_lrcorner) || |
| PRESENT(acs_ltee) || |
| PRESENT(acs_rtee) || |
| PRESENT(acs_btee) || |
| PRESENT(acs_ttee) || |
| PRESENT(acs_hline) || |
| PRESENT(acs_vline) || |
| PRESENT(acs_plus)) { |
| char buf2[MAX_TERMCAP_LENGTH]; |
| |
| _nc_str_init(&result, buf2, sizeof(buf2)); |
| _nc_safe_strcat(&result, acs_chars); |
| |
| append_acs(&result, 'j', acs_lrcorner); |
| append_acs(&result, 'k', acs_urcorner); |
| append_acs(&result, 'l', acs_ulcorner); |
| append_acs(&result, 'm', acs_llcorner); |
| append_acs(&result, 'n', acs_plus); |
| append_acs(&result, 'q', acs_hline); |
| append_acs(&result, 't', acs_ltee); |
| append_acs(&result, 'u', acs_rtee); |
| append_acs(&result, 'v', acs_btee); |
| append_acs(&result, 'w', acs_ttee); |
| append_acs(&result, 'x', acs_vline); |
| |
| if (buf2[0]) { |
| 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) { |
| acs_chars = _nc_save_str(VT_ACSC); |
| } |
| } |
| |
| static void |
| postprocess_terminfo(TERMTYPE *tp) |
| { |
| /* |
| * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION |
| * ---------------------------------------------------------------------- |
| */ |
| |
| /* |
| * Translate AIX forms characters. |
| */ |
| if (PRESENT(box_chars_1)) { |
| char buf2[MAX_TERMCAP_LENGTH]; |
| string_desc result; |
| |
| _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 */ |
| |
| if (buf2[0]) { |
| acs_chars = _nc_save_str(buf2); |
| _nc_warning("acsc string synthesized from AIX capabilities"); |
| box_chars_1 = ABSENT_STRING; |
| } |
| } |
| /* |
| * ---------------------------------------------------------------------- |
| */ |
| } |
| |
| /* |
| * Do a linear search through the terminfo tables to find a given full-name. |
| * We don't expect to do this often, so there's no hashing function. |
| * |
| * In effect, this scans through the 3 lists of full-names, and looks them |
| * up in _nc_info_table, which is organized so that the nte_index fields are |
| * sorted, but the nte_type fields are not necessarily grouped together. |
| */ |
| static struct name_table_entry const * |
| lookup_fullname(const char *find) |
| { |
| int state = -1; |
| |
| for (;;) { |
| int count = 0; |
| NCURSES_CONST char *const *names; |
| |
| switch (++state) { |
| case BOOLEAN: |
| names = boolfnames; |
| break; |
| case STRING: |
| names = strfnames; |
| break; |
| case NUMBER: |
| names = numfnames; |
| break; |
| default: |
| return NOTFOUND; |
| } |
| |
| for (count = 0; names[count] != 0; count++) { |
| if (!strcmp(names[count], find)) { |
| struct name_table_entry const *entry_ptr = _nc_get_table(FALSE); |
| while (entry_ptr->nte_type != state |
| || entry_ptr->nte_index != count) |
| entry_ptr++; |
| return entry_ptr; |
| } |
| } |
| } |
| } |
| |
| /* parse_entry.c ends here */ |