150276Speter/**************************************************************************** 2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1996-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter * parse_entry.c -- compile one terminfo or termcap entry 3750276Speter * 3850276Speter * Get an exact in-core representation of an entry. Don't 3950276Speter * try to resolve use or tc capabilities, that is someone 4050276Speter * else's job. Depends on the lexical analyzer to get tokens 4150276Speter * from the input stream. 4250276Speter */ 4350276Speter 4498503Speter#define __INTERNAL_CAPS_VISIBLE 4550276Speter#include <curses.priv.h> 4650276Speter 4750276Speter#include <ctype.h> 4850276Speter#include <tic.h> 4950276Speter#include <term_entry.h> 5050276Speter 51184989SrafanMODULE_ID("$Id: parse_entry.c,v 1.69 2008/08/16 21:52:03 tom Exp $") 5250276Speter 5350276Speter#ifdef LINT 5462449Speterstatic short const parametrized[] = 5562449Speter{0}; 5650276Speter#else 5750276Speter#include <parametrized.h> 5850276Speter#endif 5950276Speter 6062449Speterstatic void postprocess_termcap(TERMTYPE *, bool); 6162449Speterstatic void postprocess_terminfo(TERMTYPE *); 6262449Speterstatic struct name_table_entry const *lookup_fullname(const char *name); 6350276Speter 6450276Speter#if NCURSES_XNAMES 6550276Speter 6662449Speterstatic struct name_table_entry const * 6762449Speter_nc_extend_names(ENTRY * entryp, char *name, int token_type) 6850276Speter{ 6950276Speter static struct name_table_entry temp; 7050276Speter TERMTYPE *tp = &(entryp->tterm); 7150276Speter unsigned offset = 0; 7250276Speter unsigned actual; 7350276Speter unsigned tindex; 7450276Speter unsigned first, last, n; 7550276Speter bool found; 7650276Speter 7750276Speter switch (token_type) { 7850276Speter case BOOLEAN: 7962449Speter first = 0; 8062449Speter last = tp->ext_Booleans; 8150276Speter offset = tp->ext_Booleans; 8250276Speter tindex = tp->num_Booleans; 8350276Speter break; 8450276Speter case NUMBER: 8562449Speter first = tp->ext_Booleans; 8662449Speter last = tp->ext_Numbers + first; 8750276Speter offset = tp->ext_Booleans + tp->ext_Numbers; 8850276Speter tindex = tp->num_Numbers; 8950276Speter break; 9050276Speter case STRING: 9162449Speter first = tp->ext_Booleans + tp->ext_Numbers; 9262449Speter last = tp->ext_Strings + first; 9350276Speter offset = tp->ext_Booleans + tp->ext_Numbers + tp->ext_Strings; 9450276Speter tindex = tp->num_Strings; 9550276Speter break; 9650276Speter case CANCEL: 9750276Speter actual = NUM_EXT_NAMES(tp); 9850276Speter for (n = 0; n < actual; n++) { 9950276Speter if (!strcmp(name, tp->ext_Names[n])) { 10062449Speter if (n > (unsigned) (tp->ext_Booleans + tp->ext_Numbers)) { 10150276Speter token_type = STRING; 10250276Speter } else if (n > tp->ext_Booleans) { 10350276Speter token_type = NUMBER; 10450276Speter } else { 10550276Speter token_type = BOOLEAN; 10650276Speter } 10750276Speter return _nc_extend_names(entryp, name, token_type); 10850276Speter } 10950276Speter } 11050276Speter /* Well, we are given a cancel for a name that we don't recognize */ 11150276Speter return _nc_extend_names(entryp, name, STRING); 11250276Speter default: 11350276Speter return 0; 11450276Speter } 11550276Speter 11650276Speter /* Adjust the 'offset' (insertion-point) to keep the lists of extended 11750276Speter * names sorted. 11850276Speter */ 11950276Speter for (n = first, found = FALSE; n < last; n++) { 12050276Speter int cmp = strcmp(tp->ext_Names[n], name); 12150276Speter if (cmp == 0) 12250276Speter found = TRUE; 12350276Speter if (cmp >= 0) { 12450276Speter offset = n; 12550276Speter tindex = n - first; 12650276Speter switch (token_type) { 12762449Speter case BOOLEAN: 12862449Speter tindex += BOOLCOUNT; 12962449Speter break; 13062449Speter case NUMBER: 13162449Speter tindex += NUMCOUNT; 13262449Speter break; 13362449Speter case STRING: 13462449Speter tindex += STRCOUNT; 13562449Speter break; 13650276Speter } 13750276Speter break; 13850276Speter } 13950276Speter } 14050276Speter if (!found) { 14150276Speter switch (token_type) { 14250276Speter case BOOLEAN: 14350276Speter tp->ext_Booleans += 1; 14450276Speter tp->num_Booleans += 1; 145166124Srafan tp->Booleans = typeRealloc(NCURSES_SBOOL, tp->num_Booleans, tp->Booleans); 14662449Speter for (last = tp->num_Booleans - 1; last > tindex; last--) 14762449Speter tp->Booleans[last] = tp->Booleans[last - 1]; 14850276Speter break; 14950276Speter case NUMBER: 15050276Speter tp->ext_Numbers += 1; 15150276Speter tp->num_Numbers += 1; 15250276Speter tp->Numbers = typeRealloc(short, tp->num_Numbers, tp->Numbers); 15362449Speter for (last = tp->num_Numbers - 1; last > tindex; last--) 15462449Speter tp->Numbers[last] = tp->Numbers[last - 1]; 15550276Speter break; 15650276Speter case STRING: 15750276Speter tp->ext_Strings += 1; 15850276Speter tp->num_Strings += 1; 15950276Speter tp->Strings = typeRealloc(char *, tp->num_Strings, tp->Strings); 16062449Speter for (last = tp->num_Strings - 1; last > tindex; last--) 16162449Speter tp->Strings[last] = tp->Strings[last - 1]; 16250276Speter break; 16350276Speter } 16450276Speter actual = NUM_EXT_NAMES(tp); 16562449Speter tp->ext_Names = typeRealloc(char *, actual, tp->ext_Names); 16650276Speter while (--actual > offset) 16762449Speter tp->ext_Names[actual] = tp->ext_Names[actual - 1]; 16850276Speter tp->ext_Names[offset] = _nc_save_str(name); 16950276Speter } 17050276Speter 17162449Speter temp.nte_name = tp->ext_Names[offset]; 17262449Speter temp.nte_type = token_type; 17350276Speter temp.nte_index = tindex; 17462449Speter temp.nte_link = -1; 17550276Speter 17650276Speter return &temp; 17750276Speter} 17850276Speter#endif /* NCURSES_XNAMES */ 17950276Speter 18050276Speter/* 18150276Speter * int 18250276Speter * _nc_parse_entry(entry, literal, silent) 18350276Speter * 18450276Speter * Compile one entry. Doesn't try to resolve use or tc capabilities. 18550276Speter * 18650276Speter * found-forward-use = FALSE 18750276Speter * re-initialise internal arrays 18850276Speter * get_token(); 18950276Speter * if the token was not a name in column 1, complain and die 19050276Speter * save names in entry's string table 19150276Speter * while (get_token() is not EOF and not NAMES) 192166124Srafan * check for existence and type-correctness 19350276Speter * enter cap into structure 19450276Speter * if STRING 19550276Speter * save string in entry's string table 19650276Speter * push back token 19750276Speter */ 19850276Speter 199166124Srafan#define BAD_TC_USAGE if (!bad_tc_usage) \ 200166124Srafan { bad_tc_usage = TRUE; \ 201166124Srafan _nc_warning("Legacy termcap allows only a trailing tc= clause"); } 202166124Srafan 20376726SpeterNCURSES_EXPORT(int) 204166124Srafan_nc_parse_entry(struct entry *entryp, int literal, bool silent) 20550276Speter{ 20662449Speter int token_type; 20762449Speter struct name_table_entry const *entry_ptr; 20866963Speter char *ptr, *base; 209166124Srafan bool bad_tc_usage = FALSE; 21050276Speter 21176726Speter token_type = _nc_get_token(silent); 21250276Speter 21350276Speter if (token_type == EOF) 21462449Speter return (EOF); 21550276Speter if (token_type != NAMES) 21650276Speter _nc_err_abort("Entry does not start with terminal names in column one"); 21750276Speter 21850276Speter _nc_init_entry(&entryp->tterm); 21950276Speter 22050276Speter entryp->cstart = _nc_comment_start; 22150276Speter entryp->cend = _nc_comment_end; 22250276Speter entryp->startline = _nc_start_line; 22350276Speter DEBUG(2, ("Comment range is %ld to %ld", entryp->cstart, entryp->cend)); 22450276Speter 225166124Srafan /* 226166124Srafan * Strip off the 2-character termcap name, if present. Originally termcap 227166124Srafan * used that as an indexing aid. We can retain 2-character terminfo names, 228166124Srafan * but note that they would be lost if we translate to/from termcap. This 229166124Srafan * feature is supposedly obsolete since "newer" BSD implementations do not 230166124Srafan * use it; however our reference for this feature is SunOS 4.x, which 231166124Srafan * implemented it. Note that the resulting terminal type was never the 232166124Srafan * 2-character name, but was instead the first alias after that. 233166124Srafan */ 23450276Speter ptr = _nc_curr_token.tk_name; 235166124Srafan if (_nc_syntax == SYN_TERMCAP 236166124Srafan#if NCURSES_XNAMES 237166124Srafan && !_nc_user_definable 238166124Srafan#endif 239166124Srafan ) { 240166124Srafan if (ptr[2] == '|') { 241166124Srafan ptr += 3; 242166124Srafan _nc_curr_token.tk_name[2] = '\0'; 243166124Srafan } 24450276Speter } 24550276Speter 24650276Speter entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr); 24750276Speter 248184989Srafan if (entryp->tterm.str_table == 0) 249184989Srafan return (ERR); 250184989Srafan 25150276Speter DEBUG(1, ("Starting '%s'", ptr)); 25250276Speter 25350276Speter /* 25450276Speter * We do this because the one-token lookahead in the parse loop 25550276Speter * results in the terminal type getting prematurely set to correspond 25650276Speter * to that of the next entry. 25750276Speter */ 25850276Speter _nc_set_type(_nc_first_name(entryp->tterm.term_names)); 25950276Speter 26050276Speter /* check for overly-long names and aliases */ 26166963Speter for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0; 26266963Speter base = ptr + 1) { 26366963Speter if (ptr - base > MAX_ALIAS) { 26466963Speter _nc_warning("%s `%.*s' may be too long", 26566963Speter (base == entryp->tterm.term_names) 26666963Speter ? "primary name" 26766963Speter : "alias", 26897049Speter (int) (ptr - base), base); 26966963Speter } 27066963Speter } 27150276Speter 27250276Speter entryp->nuses = 0; 27350276Speter 27476726Speter for (token_type = _nc_get_token(silent); 27566963Speter token_type != EOF && token_type != NAMES; 27676726Speter token_type = _nc_get_token(silent)) { 277166124Srafan bool is_use = (strcmp(_nc_curr_token.tk_name, "use") == 0); 278166124Srafan bool is_tc = !is_use && (strcmp(_nc_curr_token.tk_name, "tc") == 0); 279166124Srafan if (is_use || is_tc) { 28062449Speter entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring); 28150276Speter entryp->uses[entryp->nuses].line = _nc_curr_line; 28250276Speter entryp->nuses++; 283166124Srafan if (entryp->nuses > 1 && is_tc) { 284166124Srafan BAD_TC_USAGE 285166124Srafan } 28650276Speter } else { 28750276Speter /* normal token lookup */ 28850276Speter entry_ptr = _nc_find_entry(_nc_curr_token.tk_name, 289174993Srafan _nc_get_hash_table(_nc_syntax)); 29050276Speter 29150276Speter /* 29250276Speter * Our kluge to handle aliasing. The reason it's done 29350276Speter * this ugly way, with a linear search, is so the hashing 29450276Speter * machinery doesn't have to be made really complicated 29550276Speter * (also we get better warnings this way). No point in 29650276Speter * making this case fast, aliased caps aren't common now 29750276Speter * and will get rarer. 29850276Speter */ 29962449Speter if (entry_ptr == NOTFOUND) { 30062449Speter const struct alias *ap; 30150276Speter 30262449Speter if (_nc_syntax == SYN_TERMCAP) { 303166124Srafan if (entryp->nuses != 0) { 304166124Srafan BAD_TC_USAGE 305166124Srafan } 306174993Srafan for (ap = _nc_get_alias_table(TRUE); ap->from; ap++) 30762449Speter if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { 30862449Speter if (ap->to == (char *) 0) { 30950276Speter _nc_warning("%s (%s termcap extension) ignored", 31066963Speter ap->from, ap->source); 31150276Speter goto nexttok; 31250276Speter } 31350276Speter 314174993Srafan entry_ptr = _nc_find_entry(ap->to, 315174993Srafan _nc_get_hash_table(TRUE)); 31650276Speter if (entry_ptr && !silent) 31762449Speter _nc_warning("%s (%s termcap extension) aliased to %s", 31866963Speter ap->from, ap->source, ap->to); 31950276Speter break; 32050276Speter } 32162449Speter } else { /* if (_nc_syntax == SYN_TERMINFO) */ 322174993Srafan for (ap = _nc_get_alias_table(FALSE); ap->from; ap++) 32362449Speter if (strcmp(ap->from, _nc_curr_token.tk_name) == 0) { 32462449Speter if (ap->to == (char *) 0) { 32550276Speter _nc_warning("%s (%s terminfo extension) ignored", 32666963Speter ap->from, ap->source); 32750276Speter goto nexttok; 32850276Speter } 32950276Speter 330174993Srafan entry_ptr = _nc_find_entry(ap->to, 331174993Srafan _nc_get_hash_table(FALSE)); 33250276Speter if (entry_ptr && !silent) 33362449Speter _nc_warning("%s (%s terminfo extension) aliased to %s", 33466963Speter ap->from, ap->source, ap->to); 33550276Speter break; 33650276Speter } 33750276Speter 33850276Speter if (entry_ptr == NOTFOUND) { 33950276Speter entry_ptr = lookup_fullname(_nc_curr_token.tk_name); 34050276Speter } 34150276Speter } 34250276Speter } 34350276Speter#if NCURSES_XNAMES 34450276Speter /* 34550276Speter * If we have extended-names active, we will automatically 34650276Speter * define a name based on its context. 34750276Speter */ 34850276Speter if (entry_ptr == NOTFOUND 34962449Speter && _nc_user_definable 35062449Speter && (entry_ptr = _nc_extend_names(entryp, 35166963Speter _nc_curr_token.tk_name, 35266963Speter token_type)) != 0) { 35362449Speter if (_nc_tracing >= DEBUG_LEVEL(1)) 35462449Speter _nc_warning("extended capability '%s'", _nc_curr_token.tk_name); 35550276Speter } 35650276Speter#endif /* NCURSES_XNAMES */ 35750276Speter 35850276Speter /* can't find this cap name, not even as an alias */ 35950276Speter if (entry_ptr == NOTFOUND) { 36050276Speter if (!silent) 36150276Speter _nc_warning("unknown capability '%s'", 36266963Speter _nc_curr_token.tk_name); 36350276Speter continue; 36450276Speter } 36550276Speter 36650276Speter /* deal with bad type/value combinations. */ 36762449Speter if (token_type != CANCEL && entry_ptr->nte_type != token_type) { 36850276Speter /* 36950276Speter * Nasty special cases here handle situations in which type 37050276Speter * information can resolve name clashes. Normal lookup 37150276Speter * finds the last instance in the capability table of a 37250276Speter * given name, regardless of type. find_type_entry looks 37350276Speter * for a first matching instance with given type. So as 37450276Speter * long as all ambiguous names occur in pairs of distinct 37550276Speter * type, this will do the job. 37650276Speter */ 37750276Speter 378166124Srafan if (token_type == NUMBER 379166124Srafan && !strcmp("ma", _nc_curr_token.tk_name)) { 380184989Srafan /* tell max_attributes from arrow_key_map */ 38150276Speter entry_ptr = _nc_find_type_entry("ma", NUMBER, 38266963Speter _nc_get_table(_nc_syntax 38366963Speter != 0)); 384184989Srafan assert(entry_ptr != 0); 38550276Speter 386166124Srafan } else if (token_type == STRING 387166124Srafan && !strcmp("MT", _nc_curr_token.tk_name)) { 388184989Srafan /* map terminfo's string MT to MT */ 38950276Speter entry_ptr = _nc_find_type_entry("MT", STRING, 39066963Speter _nc_get_table(_nc_syntax 39166963Speter != 0)); 392184989Srafan assert(entry_ptr != 0); 39350276Speter 394166124Srafan } else if (token_type == BOOLEAN 395166124Srafan && entry_ptr->nte_type == STRING) { 396184989Srafan /* treat strings without following "=" as empty strings */ 39750276Speter token_type = STRING; 398184989Srafan } else { 399166124Srafan /* we couldn't recover; skip this token */ 40062449Speter if (!silent) { 40150276Speter const char *type_name; 40262449Speter switch (entry_ptr->nte_type) { 40350276Speter case BOOLEAN: 40462449Speter type_name = "boolean"; 40562449Speter break; 40650276Speter case STRING: 40762449Speter type_name = "string"; 40862449Speter break; 40950276Speter case NUMBER: 41062449Speter type_name = "numeric"; 41162449Speter break; 41250276Speter default: 41362449Speter type_name = "unknown"; 41462449Speter break; 41550276Speter } 41650276Speter _nc_warning("wrong type used for %s capability '%s'", 41766963Speter type_name, _nc_curr_token.tk_name); 41850276Speter } 41950276Speter continue; 42050276Speter } 42150276Speter } 42250276Speter 42350276Speter /* now we know that the type/value combination is OK */ 42450276Speter switch (token_type) { 42550276Speter case CANCEL: 42650276Speter switch (entry_ptr->nte_type) { 42750276Speter case BOOLEAN: 42850276Speter entryp->tterm.Booleans[entry_ptr->nte_index] = CANCELLED_BOOLEAN; 42950276Speter break; 43050276Speter 43150276Speter case NUMBER: 43250276Speter entryp->tterm.Numbers[entry_ptr->nte_index] = CANCELLED_NUMERIC; 43350276Speter break; 43450276Speter 43550276Speter case STRING: 43650276Speter entryp->tterm.Strings[entry_ptr->nte_index] = CANCELLED_STRING; 43750276Speter break; 43850276Speter } 43950276Speter break; 44050276Speter 44150276Speter case BOOLEAN: 44250276Speter entryp->tterm.Booleans[entry_ptr->nte_index] = TRUE; 44350276Speter break; 44450276Speter 44550276Speter case NUMBER: 44650276Speter entryp->tterm.Numbers[entry_ptr->nte_index] = 44750276Speter _nc_curr_token.tk_valnumber; 44850276Speter break; 44950276Speter 45050276Speter case STRING: 45150276Speter ptr = _nc_curr_token.tk_valstring; 45262449Speter if (_nc_syntax == SYN_TERMCAP) 45350276Speter ptr = _nc_captoinfo(_nc_curr_token.tk_name, 45466963Speter ptr, 45566963Speter parametrized[entry_ptr->nte_index]); 45650276Speter entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr); 45750276Speter break; 45850276Speter 45950276Speter default: 46050276Speter if (!silent) 46150276Speter _nc_warning("unknown token type"); 462184989Srafan _nc_panic_mode((char) ((_nc_syntax == SYN_TERMCAP) ? ':' : ',')); 46350276Speter continue; 46450276Speter } 46562449Speter } /* end else cur_token.name != "use" */ 46662449Speter nexttok: 46762449Speter continue; /* cannot have a label w/o statement */ 46862449Speter } /* endwhile (not EOF and not NAMES) */ 46950276Speter 47050276Speter _nc_push_token(token_type); 47150276Speter _nc_set_type(_nc_first_name(entryp->tterm.term_names)); 47250276Speter 47350276Speter /* 47450276Speter * Try to deduce as much as possible from extension capabilities 47550276Speter * (this includes obsolete BSD capabilities). Sigh...it would be more 47650276Speter * space-efficient to call this after use resolution, but it has 47750276Speter * to be done before entry allocation is wrapped up. 47850276Speter */ 47950276Speter if (!literal) { 48062449Speter if (_nc_syntax == SYN_TERMCAP) { 48162449Speter bool has_base_entry = FALSE; 482184989Srafan unsigned i; 48350276Speter 48450276Speter /* 48550276Speter * Don't insert defaults if this is a `+' entry meant only 48650276Speter * for inclusion in other entries (not sure termcap ever 48750276Speter * had these, actually). 48850276Speter */ 48950276Speter if (strchr(entryp->tterm.term_names, '+')) 49050276Speter has_base_entry = TRUE; 49150276Speter else 49250276Speter /* 49350276Speter * Otherwise, look for a base entry that will already 49450276Speter * have picked up defaults via translation. 49550276Speter */ 49650276Speter for (i = 0; i < entryp->nuses; i++) 49762449Speter if (!strchr((char *) entryp->uses[i].name, '+')) 49850276Speter has_base_entry = TRUE; 49950276Speter 50050276Speter postprocess_termcap(&entryp->tterm, has_base_entry); 50162449Speter } else 50250276Speter postprocess_terminfo(&entryp->tterm); 50350276Speter } 50476726Speter _nc_wrap_entry(entryp, FALSE); 50550276Speter 50662449Speter return (OK); 50750276Speter} 50850276Speter 50976726SpeterNCURSES_EXPORT(int) 51062449Speter_nc_capcmp(const char *s, const char *t) 51150276Speter/* compare two string capabilities, stripping out padding */ 51250276Speter{ 51350276Speter if (!s && !t) 51462449Speter return (0); 51550276Speter else if (!s || !t) 51662449Speter return (1); 51750276Speter 51862449Speter for (;;) { 51962449Speter if (s[0] == '$' && s[1] == '<') { 52062449Speter for (s += 2;; s++) 52197049Speter if (!(isdigit(UChar(*s)) 52276726Speter || *s == '.' 52376726Speter || *s == '*' 52476726Speter || *s == '/' 52576726Speter || *s == '>')) 52650276Speter break; 52750276Speter } 52850276Speter 52962449Speter if (t[0] == '$' && t[1] == '<') { 53062449Speter for (t += 2;; t++) 53197049Speter if (!(isdigit(UChar(*t)) 53276726Speter || *t == '.' 53376726Speter || *t == '*' 53476726Speter || *t == '/' 53576726Speter || *t == '>')) 53650276Speter break; 53750276Speter } 53850276Speter 53950276Speter /* we've now pushed s and t past any padding they were pointing at */ 54050276Speter 54150276Speter if (*s == '\0' && *t == '\0') 54262449Speter return (0); 54350276Speter 54450276Speter if (*s != *t) 54562449Speter return (*t - *s); 54650276Speter 54750276Speter /* else *s == *t but one is not NUL, so continue */ 54850276Speter s++, t++; 54950276Speter } 55050276Speter} 55150276Speter 55266963Speterstatic void 55376726Speterappend_acs0(string_desc * dst, int code, int src) 55466963Speter{ 55566963Speter if (src != 0) { 55666963Speter char temp[3]; 557184989Srafan temp[0] = (char) code; 558184989Srafan temp[1] = (char) src; 55966963Speter temp[2] = 0; 56066963Speter _nc_safe_strcat(dst, temp); 56166963Speter } 56266963Speter} 56366963Speter 56466963Speterstatic void 56576726Speterappend_acs(string_desc * dst, int code, char *src) 56666963Speter{ 56766963Speter if (src != 0 && strlen(src) == 1) { 56866963Speter append_acs0(dst, code, *src); 56966963Speter } 57066963Speter} 57166963Speter 57250276Speter/* 57350276Speter * The ko capability, if present, consists of a comma-separated capability 57450276Speter * list. For each capability, we may assume there is a keycap that sends the 57550276Speter * string which is the value of that capability. 57650276Speter */ 57762449Spetertypedef struct { 57862449Speter const char *from; 57962449Speter const char *to; 58062449Speter} assoc; 58150276Speterstatic assoc const ko_xlate[] = 58250276Speter{ 58362449Speter {"al", "kil1"}, /* insert line key -> KEY_IL */ 58462449Speter {"bt", "kcbt"}, /* back tab -> KEY_BTAB */ 58562449Speter {"cd", "ked"}, /* clear-to-eos key -> KEY_EOL */ 58662449Speter {"ce", "kel"}, /* clear-to-eol key -> KEY_EOS */ 58762449Speter {"cl", "kclr"}, /* clear key -> KEY_CLEAR */ 58862449Speter {"ct", "tbc"}, /* clear all tabs -> KEY_CATAB */ 58962449Speter {"dc", "kdch1"}, /* delete char -> KEY_DC */ 59062449Speter {"dl", "kdl1"}, /* delete line -> KEY_DL */ 59162449Speter {"do", "kcud1"}, /* down key -> KEY_DOWN */ 59262449Speter {"ei", "krmir"}, /* exit insert key -> KEY_EIC */ 59362449Speter {"ho", "khome"}, /* home key -> KEY_HOME */ 59462449Speter {"ic", "kich1"}, /* insert char key -> KEY_IC */ 59562449Speter {"im", "kIC"}, /* insert-mode key -> KEY_SIC */ 59662449Speter {"le", "kcub1"}, /* le key -> KEY_LEFT */ 59762449Speter {"nd", "kcuf1"}, /* nd key -> KEY_RIGHT */ 59862449Speter {"nl", "kent"}, /* new line key -> KEY_ENTER */ 59962449Speter {"st", "khts"}, /* set-tab key -> KEY_STAB */ 60062449Speter {"ta", CANCELLED_STRING}, 60162449Speter {"up", "kcuu1"}, /* up-arrow key -> KEY_UP */ 60262449Speter {(char *) 0, (char *) 0}, 60350276Speter}; 60450276Speter 60550276Speter/* 60650276Speter * This routine fills in string caps that either had defaults under 60750276Speter * termcap or can be manufactured from obsolete termcap capabilities. 60850276Speter * It was lifted from Ross Ridge's mytinfo package. 60950276Speter */ 61050276Speter 61150276Speterstatic const char C_CR[] = "\r"; 61250276Speterstatic const char C_LF[] = "\n"; 61350276Speterstatic const char C_BS[] = "\b"; 61450276Speterstatic const char C_HT[] = "\t"; 61550276Speter 61650276Speter/* 61750276Speter * Note that WANTED and PRESENT are not simple inverses! If a capability 61850276Speter * has been explicitly cancelled, it's not considered WANTED. 61950276Speter */ 62050276Speter#define WANTED(s) ((s) == ABSENT_STRING) 62150276Speter#define PRESENT(s) (((s) != ABSENT_STRING) && ((s) != CANCELLED_STRING)) 62250276Speter 62350276Speter/* 62450276Speter * This bit of legerdemain turns all the terminfo variable names into 62550276Speter * references to locations in the arrays Booleans, Numbers, and Strings --- 62650276Speter * precisely what's needed. 62750276Speter */ 62850276Speter 62950276Speter#undef CUR 63050276Speter#define CUR tp-> 63150276Speter 63266963Speterstatic void 633166124Srafanpostprocess_termcap(TERMTYPE *tp, bool has_base) 63450276Speter{ 63550276Speter char buf[MAX_LINE * 2 + 2]; 63666963Speter string_desc result; 63750276Speter 63850276Speter /* 63950276Speter * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS 64050276Speter * 64150276Speter * This first part of the code is the functional inverse of the 64250276Speter * fragment in capdefaults.c. 64350276Speter * ---------------------------------------------------------------------- 64450276Speter */ 64550276Speter 64650276Speter /* if there was a tc entry, assume we picked up defaults via that */ 64762449Speter if (!has_base) { 64850276Speter if (WANTED(init_3string) && termcap_init2) 64950276Speter init_3string = _nc_save_str(termcap_init2); 65050276Speter 65150276Speter if (WANTED(reset_2string) && termcap_reset) 65250276Speter reset_2string = _nc_save_str(termcap_reset); 65350276Speter 65450276Speter if (WANTED(carriage_return)) { 65550276Speter if (carriage_return_delay > 0) { 65650276Speter sprintf(buf, "%s$<%d>", C_CR, carriage_return_delay); 65750276Speter carriage_return = _nc_save_str(buf); 65850276Speter } else 65950276Speter carriage_return = _nc_save_str(C_CR); 66050276Speter } 66150276Speter if (WANTED(cursor_left)) { 66250276Speter if (backspace_delay > 0) { 66350276Speter sprintf(buf, "%s$<%d>", C_BS, backspace_delay); 66450276Speter cursor_left = _nc_save_str(buf); 66550276Speter } else if (backspaces_with_bs == 1) 66650276Speter cursor_left = _nc_save_str(C_BS); 66750276Speter else if (PRESENT(backspace_if_not_bs)) 66850276Speter cursor_left = backspace_if_not_bs; 66950276Speter } 67050276Speter /* vi doesn't use "do", but it does seems to use nl (or '\n') instead */ 67150276Speter if (WANTED(cursor_down)) { 67250276Speter if (PRESENT(linefeed_if_not_lf)) 67350276Speter cursor_down = linefeed_if_not_lf; 67450276Speter else if (linefeed_is_newline != 1) { 67550276Speter if (new_line_delay > 0) { 67650276Speter sprintf(buf, "%s$<%d>", C_LF, new_line_delay); 67750276Speter cursor_down = _nc_save_str(buf); 67850276Speter } else 67950276Speter cursor_down = _nc_save_str(C_LF); 68050276Speter } 68150276Speter } 68250276Speter if (WANTED(scroll_forward) && crt_no_scrolling != 1) { 68350276Speter if (PRESENT(linefeed_if_not_lf)) 68450276Speter cursor_down = linefeed_if_not_lf; 68550276Speter else if (linefeed_is_newline != 1) { 68650276Speter if (new_line_delay > 0) { 68750276Speter sprintf(buf, "%s$<%d>", C_LF, new_line_delay); 68850276Speter scroll_forward = _nc_save_str(buf); 68950276Speter } else 69050276Speter scroll_forward = _nc_save_str(C_LF); 69150276Speter } 69250276Speter } 69350276Speter if (WANTED(newline)) { 69450276Speter if (linefeed_is_newline == 1) { 69550276Speter if (new_line_delay > 0) { 69650276Speter sprintf(buf, "%s$<%d>", C_LF, new_line_delay); 69750276Speter newline = _nc_save_str(buf); 69850276Speter } else 69950276Speter newline = _nc_save_str(C_LF); 70050276Speter } else if (PRESENT(carriage_return) && PRESENT(scroll_forward)) { 70166963Speter _nc_str_init(&result, buf, sizeof(buf)); 70266963Speter if (_nc_safe_strcat(&result, carriage_return) 70376726Speter && _nc_safe_strcat(&result, scroll_forward)) 70466963Speter newline = _nc_save_str(buf); 70550276Speter } else if (PRESENT(carriage_return) && PRESENT(cursor_down)) { 70666963Speter _nc_str_init(&result, buf, sizeof(buf)); 70766963Speter if (_nc_safe_strcat(&result, carriage_return) 70876726Speter && _nc_safe_strcat(&result, cursor_down)) 70966963Speter newline = _nc_save_str(buf); 71050276Speter } 71150276Speter } 71250276Speter } 71350276Speter 71450276Speter /* 71550276Speter * Inverse of capdefaults.c code ends here. 71650276Speter * ---------------------------------------------------------------------- 71750276Speter * 71850276Speter * TERMCAP-TO TERMINFO MAPPINGS FOR SOURCE TRANSLATION 71950276Speter * 72050276Speter * These translations will *not* be inverted by tgetent(). 72150276Speter */ 72250276Speter 72362449Speter if (!has_base) { 72450276Speter /* 72550276Speter * We wait until now to decide if we've got a working cr because even 72650276Speter * one that doesn't work can be used for newline. Unfortunately the 72750276Speter * space allocated for it is wasted. 72850276Speter */ 72950276Speter if (return_does_clr_eol == 1 || no_correctly_working_cr == 1) 73050276Speter carriage_return = ABSENT_STRING; 73150276Speter 73250276Speter /* 73350276Speter * Supposedly most termcap entries have ta now and '\t' is no longer a 73450276Speter * default, but it doesn't seem to be true... 73550276Speter */ 73650276Speter if (WANTED(tab)) { 73750276Speter if (horizontal_tab_delay > 0) { 73850276Speter sprintf(buf, "%s$<%d>", C_HT, horizontal_tab_delay); 73950276Speter tab = _nc_save_str(buf); 74050276Speter } else 74150276Speter tab = _nc_save_str(C_HT); 74250276Speter } 74350276Speter if (init_tabs == ABSENT_NUMERIC && has_hardware_tabs == TRUE) 74450276Speter init_tabs = 8; 74550276Speter 74650276Speter /* 74750276Speter * Assume we can beep with ^G unless we're given bl@. 74850276Speter */ 74950276Speter if (WANTED(bell)) 75050276Speter bell = _nc_save_str("\007"); 75150276Speter } 75250276Speter 75350276Speter /* 75450276Speter * Translate the old termcap :pt: capability to it#8 + ht=\t 75550276Speter */ 75650276Speter if (has_hardware_tabs == TRUE) { 75750276Speter if (init_tabs != 8 && init_tabs != ABSENT_NUMERIC) 75850276Speter _nc_warning("hardware tabs with a width other than 8: %d", init_tabs); 75962449Speter else { 76050276Speter if (tab && _nc_capcmp(tab, C_HT)) 76150276Speter _nc_warning("hardware tabs with a non-^I tab string %s", 76266963Speter _nc_visbuf(tab)); 76362449Speter else { 76450276Speter if (WANTED(tab)) 76550276Speter tab = _nc_save_str(C_HT); 76650276Speter init_tabs = 8; 76750276Speter } 76850276Speter } 76950276Speter } 77050276Speter /* 77150276Speter * Now translate the ko capability, if there is one. This 77250276Speter * isn't from mytinfo... 77350276Speter */ 77462449Speter if (PRESENT(other_non_function_keys)) { 77566963Speter char *base = other_non_function_keys; 77666963Speter char *bp, *cp, *dp; 77762449Speter struct name_table_entry const *from_ptr; 77862449Speter struct name_table_entry const *to_ptr; 77962449Speter assoc const *ap; 78062449Speter char buf2[MAX_TERMINFO_LENGTH]; 78162449Speter bool foundim; 78250276Speter 78350276Speter /* we're going to use this for a special case later */ 78450276Speter dp = strchr(other_non_function_keys, 'i'); 78566963Speter foundim = (dp != 0) && (dp[1] == 'm'); 78650276Speter 78750276Speter /* look at each comma-separated capability in the ko string... */ 78866963Speter for (base = other_non_function_keys; 78966963Speter (cp = strchr(base, ',')) != 0; 79066963Speter base = cp + 1) { 79166963Speter size_t len = cp - base; 79266963Speter 793184989Srafan for (ap = ko_xlate; ap->from; ap++) { 79466963Speter if (len == strlen(ap->from) 79566963Speter && strncmp(ap->from, base, len) == 0) 79650276Speter break; 797184989Srafan } 798184989Srafan if (!(ap->from && ap->to)) { 79966963Speter _nc_warning("unknown capability `%.*s' in ko string", 80066963Speter (int) len, base); 80150276Speter continue; 80262449Speter } else if (ap->to == CANCELLED_STRING) /* ignore it */ 80350276Speter continue; 80450276Speter 80550276Speter /* now we know we found a match in ko_table, so... */ 80650276Speter 807174993Srafan from_ptr = _nc_find_entry(ap->from, _nc_get_hash_table(TRUE)); 808174993Srafan to_ptr = _nc_find_entry(ap->to, _nc_get_hash_table(FALSE)); 80950276Speter 81050276Speter if (!from_ptr || !to_ptr) /* should never happen! */ 81150276Speter _nc_err_abort("ko translation table is invalid, I give up"); 81250276Speter 81362449Speter if (WANTED(tp->Strings[from_ptr->nte_index])) { 81450276Speter _nc_warning("no value for ko capability %s", ap->from); 81550276Speter continue; 81650276Speter } 81750276Speter 81862449Speter if (tp->Strings[to_ptr->nte_index]) { 81950276Speter /* There's no point in warning about it if it's the same 82050276Speter * string; that's just an inefficiency. 82150276Speter */ 82250276Speter if (strcmp( 82366963Speter tp->Strings[from_ptr->nte_index], 82466963Speter tp->Strings[to_ptr->nte_index]) != 0) 82550276Speter _nc_warning("%s (%s) already has an explicit value %s, ignoring ko", 82666963Speter ap->to, ap->from, 82766963Speter _nc_visbuf(tp->Strings[to_ptr->nte_index])); 82850276Speter continue; 82950276Speter } 83050276Speter 83150276Speter /* 83250276Speter * The magic moment -- copy the mapped key string over, 83350276Speter * stripping out padding. 83450276Speter */ 83566963Speter for (dp = buf2, bp = tp->Strings[from_ptr->nte_index]; *bp; bp++) { 83666963Speter if (bp[0] == '$' && bp[1] == '<') { 83766963Speter while (*bp && *bp != '>') { 83866963Speter ++bp; 83966963Speter } 84062449Speter } else 84166963Speter *dp++ = *bp; 84250276Speter } 84350276Speter *dp++ = '\0'; 84450276Speter 84550276Speter tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2); 84666963Speter } 84750276Speter 84850276Speter /* 84950276Speter * Note: ko=im and ko=ic both want to grab the `Insert' 85050276Speter * keycap. There's a kich1 but no ksmir, so the ic capability 85150276Speter * got mapped to kich1 and im to kIC to avoid a collision. 85250276Speter * If the description has im but not ic, hack kIC back to kich1. 85350276Speter */ 85462449Speter if (foundim && WANTED(key_ic) && key_sic) { 85550276Speter key_ic = key_sic; 85650276Speter key_sic = ABSENT_STRING; 85750276Speter } 85850276Speter } 85950276Speter 86076726Speter if (!has_base) { 86176726Speter if (!hard_copy) { 86276726Speter if (WANTED(key_backspace)) 86376726Speter key_backspace = _nc_save_str(C_BS); 86476726Speter if (WANTED(key_left)) 86576726Speter key_left = _nc_save_str(C_BS); 86676726Speter if (WANTED(key_down)) 86776726Speter key_down = _nc_save_str(C_LF); 86876726Speter } 86950276Speter } 87050276Speter 87150276Speter /* 87250276Speter * Translate XENIX forms characters. 87350276Speter */ 87450276Speter if (PRESENT(acs_ulcorner) || 87550276Speter PRESENT(acs_llcorner) || 87650276Speter PRESENT(acs_urcorner) || 87750276Speter PRESENT(acs_lrcorner) || 87850276Speter PRESENT(acs_ltee) || 87950276Speter PRESENT(acs_rtee) || 88050276Speter PRESENT(acs_btee) || 88150276Speter PRESENT(acs_ttee) || 88250276Speter PRESENT(acs_hline) || 88350276Speter PRESENT(acs_vline) || 88462449Speter PRESENT(acs_plus)) { 88566963Speter char buf2[MAX_TERMCAP_LENGTH]; 88650276Speter 88766963Speter _nc_str_init(&result, buf2, sizeof(buf2)); 88866963Speter _nc_safe_strcat(&result, acs_chars); 88950276Speter 89076726Speter append_acs(&result, 'j', acs_lrcorner); 89176726Speter append_acs(&result, 'k', acs_urcorner); 89276726Speter append_acs(&result, 'l', acs_ulcorner); 89376726Speter append_acs(&result, 'm', acs_llcorner); 89476726Speter append_acs(&result, 'n', acs_plus); 89576726Speter append_acs(&result, 'q', acs_hline); 89676726Speter append_acs(&result, 't', acs_ltee); 89776726Speter append_acs(&result, 'u', acs_rtee); 89876726Speter append_acs(&result, 'v', acs_btee); 89976726Speter append_acs(&result, 'w', acs_ttee); 90076726Speter append_acs(&result, 'x', acs_vline); 90150276Speter 90266963Speter if (buf2[0]) { 90350276Speter acs_chars = _nc_save_str(buf2); 90450276Speter _nc_warning("acsc string synthesized from XENIX capabilities"); 90550276Speter } 90662449Speter } else if (acs_chars == 0 90766963Speter && enter_alt_charset_mode != 0 90866963Speter && exit_alt_charset_mode != 0) { 909166124Srafan acs_chars = _nc_save_str(VT_ACSC); 91050276Speter } 91150276Speter} 91250276Speter 91366963Speterstatic void 914166124Srafanpostprocess_terminfo(TERMTYPE *tp) 91550276Speter{ 91650276Speter /* 91750276Speter * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION 91850276Speter * ---------------------------------------------------------------------- 91950276Speter */ 92050276Speter 92150276Speter /* 92250276Speter * Translate AIX forms characters. 92350276Speter */ 92462449Speter if (PRESENT(box_chars_1)) { 92566963Speter char buf2[MAX_TERMCAP_LENGTH]; 92666963Speter string_desc result; 92750276Speter 92866963Speter _nc_str_init(&result, buf2, sizeof(buf2)); 92966963Speter _nc_safe_strcat(&result, acs_chars); 93050276Speter 93176726Speter append_acs0(&result, 'l', box_chars_1[0]); /* ACS_ULCORNER */ 93276726Speter append_acs0(&result, 'q', box_chars_1[1]); /* ACS_HLINE */ 93376726Speter append_acs0(&result, 'k', box_chars_1[2]); /* ACS_URCORNER */ 93476726Speter append_acs0(&result, 'x', box_chars_1[3]); /* ACS_VLINE */ 93576726Speter append_acs0(&result, 'j', box_chars_1[4]); /* ACS_LRCORNER */ 93676726Speter append_acs0(&result, 'm', box_chars_1[5]); /* ACS_LLCORNER */ 93776726Speter append_acs0(&result, 'w', box_chars_1[6]); /* ACS_TTEE */ 93876726Speter append_acs0(&result, 'u', box_chars_1[7]); /* ACS_RTEE */ 93976726Speter append_acs0(&result, 'v', box_chars_1[8]); /* ACS_BTEE */ 94076726Speter append_acs0(&result, 't', box_chars_1[9]); /* ACS_LTEE */ 94176726Speter append_acs0(&result, 'n', box_chars_1[10]); /* ACS_PLUS */ 94250276Speter 94366963Speter if (buf2[0]) { 94450276Speter acs_chars = _nc_save_str(buf2); 94550276Speter _nc_warning("acsc string synthesized from AIX capabilities"); 94650276Speter box_chars_1 = ABSENT_STRING; 94750276Speter } 94850276Speter } 94950276Speter /* 95050276Speter * ---------------------------------------------------------------------- 95150276Speter */ 95250276Speter} 95350276Speter 95450276Speter/* 95550276Speter * Do a linear search through the terminfo tables to find a given full-name. 95650276Speter * We don't expect to do this often, so there's no hashing function. 95750276Speter * 95850276Speter * In effect, this scans through the 3 lists of full-names, and looks them 95950276Speter * up in _nc_info_table, which is organized so that the nte_index fields are 96050276Speter * sorted, but the nte_type fields are not necessarily grouped together. 96150276Speter */ 96266963Speterstatic struct name_table_entry const * 96362449Speterlookup_fullname(const char *find) 96450276Speter{ 96550276Speter int state = -1; 96650276Speter 96750276Speter for (;;) { 96850276Speter int count = 0; 96950276Speter NCURSES_CONST char *const *names; 97050276Speter 97150276Speter switch (++state) { 97250276Speter case BOOLEAN: 97350276Speter names = boolfnames; 97450276Speter break; 97550276Speter case STRING: 97650276Speter names = strfnames; 97750276Speter break; 97850276Speter case NUMBER: 97950276Speter names = numfnames; 98050276Speter break; 98150276Speter default: 98250276Speter return NOTFOUND; 98350276Speter } 98450276Speter 98550276Speter for (count = 0; names[count] != 0; count++) { 98650276Speter if (!strcmp(names[count], find)) { 98762449Speter struct name_table_entry const *entry_ptr = _nc_get_table(FALSE); 98862449Speter while (entry_ptr->nte_type != state 98966963Speter || entry_ptr->nte_index != count) 99062449Speter entry_ptr++; 99150276Speter return entry_ptr; 99250276Speter } 99350276Speter } 99450276Speter } 99550276Speter} 99650276Speter 99750276Speter/* parse_entry.c ends here */ 998