_nc_set_writedir(char *dir) /* set the write directory for compiled entries */ { const char *destination; char actual[PATH_MAX]; if (dir == 0 && use_terminfo_vars()) dir = getenv("TERMINFO"); if (dir != 0) (void) _nc_tic_dir(dir); destination = _nc_tic_dir(0); if (make_directory(destination) < 0) { char *home = _nc_home_terminfo(); if (home != 0) { destination = home; if (make_directory(destination) < 0) _nc_err_abort("%s: permission denied (errno %d)", destination, errno); } } /* * Note: because of this code, this logic should be exercised * *once only* per run. */ if (chdir(_nc_tic_dir(destination)) < 0 || getcwd(actual, sizeof(actual)) == 0) _nc_err_abort("%s: not a directory", destination); _nc_keep_tic_dir(strdup(actual)); }
_nc_read_entry_source(FILE *fp, char *buf, int literal, bool silent, bool(*hook) (ENTRY *)) /* slurp all entries in the given file into core */ { ENTRY thisentry; bool oldsuppress = _nc_suppress_warnings; int immediate = 0; if (silent) _nc_suppress_warnings = TRUE; /* shut the lexer up, too */ _nc_reset_input(fp, buf); for (;;) { memset(&thisentry, 0, sizeof(thisentry)); if (_nc_parse_entry(&thisentry, literal, silent) == ERR) break; if (!isalnum(UChar(thisentry.tterm.term_names[0]))) _nc_err_abort("terminal names must start with letter or digit"); /* * This can be used for immediate compilation of entries with no "use=" * references to disk. That avoids consuming a lot of memory when the * resolution code could fetch entries off disk. */ if (hook != NULLHOOK && (*hook) (&thisentry)) { immediate++; } else { enqueue(&thisentry); /* * The enqueued entry is copied with _nc_copy_termtype(), so we can * free some of the data from thisentry, i.e., the arrays. */ FreeIfNeeded(thisentry.tterm.Booleans); FreeIfNeeded(thisentry.tterm.Numbers); FreeIfNeeded(thisentry.tterm.Strings); #if NCURSES_XNAMES FreeIfNeeded(thisentry.tterm.ext_Names); #endif } } if (_nc_tail) { /* set up the head pointer */ for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) continue; DEBUG(1, ("head = %s", _nc_head->tterm.term_names)); DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names)); } #ifdef TRACE else if (!immediate) DEBUG(1, ("no entries parsed")); #endif _nc_suppress_warnings = oldsuppress; }
static inline void get_space(size_t need) { need += out_used; if (need > out_size) { out_size = need * 2; out_buff = typeRealloc(char, out_size, out_buff); if (out_buff == 0) _nc_err_abort(MSG_NO_MEMORY); }
static char * init_string(void) /* initialize 'my_string', 'my_length' */ { if (my_string == 0) my_string = typeMalloc(char, my_length = 256); if (my_string == 0) _nc_err_abort(MSG_NO_MEMORY); *my_string = '\0'; return my_string; }
_nc_set_writedir(char *dir) { const char *destination; char actual[PATH_MAX]; if (dir == 0 #ifndef USE_ROOT_ENVIRON && use_terminfo_vars() #endif ) dir = getenv("TERMINFO"); if (dir != 0) (void) _nc_tic_dir(dir); destination = _nc_tic_dir(0); if (make_db_root(destination) < 0) { char *home = _nc_home_terminfo(); if (home != 0) { destination = home; if (make_db_root(destination) < 0) _nc_err_abort("%s: permission denied (errno %d)", destination, errno); } } /* * Note: because of this code, this logic should be exercised * *once only* per run. */ #if USE_HASHED_DB make_db_path(actual, destination, sizeof(actual)); #else if (chdir(_nc_tic_dir(destination)) < 0 || getcwd(actual, sizeof(actual)) == 0) _nc_err_abort("%s: not a directory", destination); #endif _nc_keep_tic_dir(strdup(actual)); }
/* * Check for access rights to destination directories * Create any directories which don't exist. * * Note: there's no reason to return the result of make_db_root(), since * this function is called only in instances where that has to succeed. */ static void check_writeable(int code) { static const char dirnames[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; static bool verified[sizeof(dirnames)]; char dir[sizeof(LEAF_FMT)]; char *s = 0; if (code == 0 || (s = strchr(dirnames, code)) == 0) _nc_err_abort("Illegal terminfo subdirectory \"" LEAF_FMT "\"", code); if (verified[s - dirnames]) return; snprintf(dir, sizeof(dir), LEAF_FMT, code); if (make_db_root(dir) < 0) { _nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir); } verified[s - dirnames] = TRUE; }
void _nc_set_writedir(char *dir) /* set the write directory for compiled entries */ { const char *destination; if (dir != 0) (void) _nc_tic_dir(dir); else if (getenv("TERMINFO") != NULL) (void) _nc_tic_dir(getenv("TERMINFO")); destination = _nc_tic_dir(0); if (make_directory(destination) < 0) { char *home; /* ncurses extension...fall back on user's private directory */ if ((home = getenv("HOME")) != (char *)NULL && strlen(home) + sizeof(PRIVATE_INFO) <= PATH_MAX) { char *temp = malloc(sizeof(PRIVATE_INFO) + strlen(home)); if (temp == NULL) _nc_err_abort("Out of memory"); (void) sprintf(temp, PRIVATE_INFO, home); destination = temp; if (make_directory(destination) < 0) _nc_err_abort("%s: permission denied (errno %d)", destination, errno); } } /* * Note: because of this code, this logic should be exercised * *once only* per run. */ if (chdir(_nc_tic_dir(destination)) < 0) _nc_err_abort("%s: not a directory", destination); }
static char * save_string(char *d, const char *const s) { size_t have = (size_t) (d - my_string); size_t need = have + strlen(s) + 2; if (need > my_length) { my_string = (char *) _nc_doalloc(my_string, my_length = (need + need)); if (my_string == 0) _nc_err_abort(MSG_NO_MEMORY); d = my_string + have; } _nc_STRCPY(d, s, my_length - have); return d + strlen(d); }
static char * save_string(char *d, const char *const s) { size_t have = (d - my_string); size_t need = have + strlen(s) + 2; if (need > my_length) { my_string = (char *) realloc(my_string, my_length = (need + need)); if (my_string == 0) _nc_err_abort(MSG_NO_MEMORY); d = my_string + have; } (void) strcpy(d, s); return d + strlen(d); }
static void enqueue(ENTRY * ep) /* add an entry to the in-core list */ { ENTRY *newp = _nc_copy_entry(ep); if (newp == 0) _nc_err_abort(MSG_NO_MEMORY); newp->last = _nc_tail; _nc_tail = newp; newp->next = 0; if (newp->last) newp->last->next = newp; }
static void file_comparison(int argc, char *argv[]) { #define MAXCOMPARE 2 /* someday we may allow comparisons on more files */ int filecount = 0; ENTRY *heads[MAXCOMPARE]; ENTRY *tails[MAXCOMPARE]; ENTRY *qp, *rp; int i, n; dump_init((char *)NULL, F_LITERAL, S_TERMINFO, 0, itrace, FALSE); for (n = 0; n < argc && n < MAXCOMPARE; n++) { if (freopen(argv[n], "r", stdin) == NULL) _nc_err_abort("Can't open %s", argv[n]); _nc_head = _nc_tail = (ENTRY *)NULL; /* parse entries out of the source file */ _nc_set_source(argv[n]); _nc_read_entry_source(stdin, NULL, TRUE, FALSE, NULLHOOK); if (itrace) (void) fprintf(stderr, "Resolving file %d...\n", n-0); /* do use resolution */ if (!_nc_resolve_uses()) { (void) fprintf(stderr, "There are unresolved use entries in %s:\n", argv[n]); for_entry_list(qp) if (qp->nuses) { (void) fputs(qp->tterm.term_names, stderr); (void) fputc('\n', stderr); } exit(EXIT_FAILURE); } heads[filecount] = _nc_head; tails[filecount] = _nc_tail; filecount++; }
_nc_home_terminfo(void) { char *result = 0; #if USE_HOME_TERMINFO char *home; if (use_terminfo_vars()) { if (MyBuffer == 0) { if ((home = getenv("HOME")) != 0) { unsigned want = (strlen(home) + sizeof(PRIVATE_INFO)); MyBuffer = typeMalloc(char, want); if (MyBuffer == 0) _nc_err_abort(MSG_NO_MEMORY); (void) sprintf(MyBuffer, PRIVATE_INFO, home); } } result = MyBuffer; }
char * _nc_home_terminfo(void) { char *home; static char *temp = 0; if (use_terminfo_vars()) { if (temp == 0) { if ((home = getenv("HOME")) != 0 && my_length <= PATH_MAX) { temp = typeMalloc(char, my_length); if (temp == 0) _nc_err_abort("Out of memory"); (void) sprintf(temp, PRIVATE_INFO, home); } } return temp; }
_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); 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 {
_nc_resolve_uses2(bool fullresolve, bool literal) /* try to resolve all use capabilities */ { ENTRY *qp, *rp, *lastread = 0; bool keepgoing; unsigned i; int unresolved, total_unresolved, multiples; DEBUG(2, ("RESOLUTION BEGINNING")); /* * Check for multiple occurrences of the same name. */ multiples = 0; for_entry_list(qp) { int matchcount = 0; for_entry_list(rp) { if (qp > rp && _nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) { matchcount++; if (matchcount == 1) { (void) fprintf(stderr, "Name collision between %s", _nc_first_name(qp->tterm.term_names)); multiples++; } if (matchcount >= 1) (void) fprintf(stderr, " %s", _nc_first_name(rp->tterm.term_names)); } } if (matchcount >= 1) (void) putc('\n', stderr); } if (multiples > 0) return (FALSE); DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES")); /* * First resolution stage: compute link pointers corresponding to names. */ total_unresolved = 0; _nc_curr_col = -1; for_entry_list(qp) { unresolved = 0; for (i = 0; i < qp->nuses; i++) { bool foundit; char *child = _nc_first_name(qp->tterm.term_names); char *lookfor = qp->uses[i].name; long lookline = qp->uses[i].line; foundit = FALSE; _nc_set_type(child); /* first, try to resolve from in-core records */ for_entry_list(rp) { if (rp != qp && _nc_name_match(rp->tterm.term_names, lookfor, "|")) { DEBUG(2, ("%s: resolving use=%s (in core)", child, lookfor)); qp->uses[i].link = rp; foundit = TRUE; } } /* if that didn't work, try to merge in a compiled entry */ if (!foundit) { TERMTYPE thisterm; char filename[PATH_MAX]; memset(&thisterm, 0, sizeof(thisterm)); if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { DEBUG(2, ("%s: resolving use=%s (compiled)", child, lookfor)); rp = typeMalloc(ENTRY, 1); if (rp == 0) _nc_err_abort(MSG_NO_MEMORY); rp->tterm = thisterm; rp->nuses = 0; rp->next = lastread; lastread = rp; qp->uses[i].link = rp; foundit = TRUE; } } /* no good, mark this one unresolvable and complain */ if (!foundit) { unresolved++; total_unresolved++; _nc_curr_line = lookline; _nc_warning("resolution of use=%s failed", lookfor); qp->uses[i].link = 0; } } } if (total_unresolved) { /* free entries read in off disk */ _nc_free_entries(lastread); return (FALSE); } DEBUG(2, ("NAME RESOLUTION COMPLETED OK")); /* * OK, at this point all (char *) references in `name' members * have been successfully converted to (ENTRY *) pointers in * `link' members. Time to do the actual merges. */ if (fullresolve) { do { TERMTYPE merged; keepgoing = FALSE; for_entry_list(qp) { if (qp->nuses > 0) { DEBUG(2, ("%s: attempting merge", _nc_first_name(qp->tterm.term_names))); /* * If any of the use entries we're looking for is * incomplete, punt. We'll catch this entry on a * subsequent pass. */ for (i = 0; i < qp->nuses; i++) if (qp->uses[i].link->nuses) { DEBUG(2, ("%s: use entry %d unresolved", _nc_first_name(qp->tterm.term_names), i)); goto incomplete; } /* * First, make sure there is no garbage in the * merge block. As a side effect, copy into * the merged entry the name field and string * table pointer. */ _nc_copy_termtype(&merged, &(qp->tterm)); /* * Now merge in each use entry in the proper * (reverse) order. */ for (; qp->nuses; qp->nuses--) _nc_merge_entry(&merged, &qp->uses[qp->nuses - 1].link->tterm); /* * Now merge in the original entry. */ _nc_merge_entry(&merged, &qp->tterm); /* * Replace the original entry with the merged one. */ FreeIfNeeded(qp->tterm.Booleans); FreeIfNeeded(qp->tterm.Numbers); FreeIfNeeded(qp->tterm.Strings); #if NCURSES_XNAMES FreeIfNeeded(qp->tterm.ext_Names); #endif qp->tterm = merged; _nc_wrap_entry(qp, TRUE); /* * We know every entry is resolvable because name resolution * didn't bomb. So go back for another pass. */ /* FALLTHRU */ incomplete: keepgoing = TRUE; } } } while (keepgoing); DEBUG(2, ("MERGES COMPLETED OK")); } /* * We'd like to free entries read in off disk at this point, but can't. * The merge_entry() code doesn't copy the strings in the use entries, * it just aliases them. If this ever changes, do a * free_entries(lastread) here. */ DEBUG(2, ("RESOLUTION FINISHED")); if (fullresolve) if (_nc_check_termtype != 0) { _nc_curr_col = -1; for_entry_list(qp) { _nc_curr_line = qp->startline; _nc_set_type(_nc_first_name(qp->tterm.term_names)); _nc_check_termtype2(&qp->tterm, literal); } DEBUG(2, ("SANITY CHECK FINISHED")); }
_nc_get_token(bool silent) { static const char terminfo_punct[] = "@%&*!#"; long number; int type; int ch; char *numchk; char numbuf[80]; unsigned found; static char buffer[MAX_ENTRY_SIZE]; char *ptr; int dot_flag = FALSE; long token_start; if (pushtype != NO_PUSHBACK) { int retval = pushtype; _nc_set_type(pushname); DEBUG(3, ("pushed-back token: `%s', class %d", _nc_curr_token.tk_name, pushtype)); pushtype = NO_PUSHBACK; pushname[0] = '\0'; /* currtok wasn't altered by _nc_push_token() */ return (retval); } if (end_of_stream()) return (EOF); start_token: token_start = stream_pos(); while ((ch = next_char()) == '\n' || iswhite(ch)) continue; ch = eat_escaped_newline(ch); if (ch == EOF) type = EOF; else { /* if this is a termcap entry, skip a leading separator */ if (separator == ':' && ch == ':') ch = next_char(); if (ch == '.' #if NCURSES_EXT_FUNCS && !_nc_disable_period #endif ) { dot_flag = TRUE; DEBUG(8, ("dot-flag set")); while ((ch = next_char()) == '.' || iswhite(ch)) continue; } if (ch == EOF) { type = EOF; goto end_of_token; } /* have to make some punctuation chars legal for terminfo */ if (!isalnum(ch) #if NCURSES_EXT_FUNCS && !(ch == '.' && _nc_disable_period) #endif && !strchr(terminfo_punct, (char) ch)) { if (!silent) _nc_warning("Illegal character (expected alphanumeric or %s) - %s", terminfo_punct, unctrl((chtype) ch)); _nc_panic_mode(separator); goto start_token; } ptr = buffer; *(ptr++) = ch; if (first_column) { char *desc; _nc_comment_start = token_start; _nc_comment_end = _nc_curr_file_pos; _nc_start_line = _nc_curr_line; _nc_syntax = ERR; while ((ch = next_char()) != '\n') { if (ch == EOF) _nc_err_abort("premature EOF"); else if (ch == ':' && last_char() != ',') { _nc_syntax = SYN_TERMCAP; separator = ':'; break; } else if (ch == ',') { _nc_syntax = SYN_TERMINFO; separator = ','; /* * Fall-through here is not an accident. The idea is that * if we see a comma, we figure this is terminfo unless we * subsequently run into a colon -- but we don't stop * looking for that colon until hitting a newline. This * allows commas to be embedded in description fields of * either syntax. */ /* FALLTHRU */ } else ch = eat_escaped_newline(ch); *ptr++ = ch; } ptr[0] = '\0'; if (_nc_syntax == ERR) { /* * Grrr...what we ought to do here is barf, complaining that * the entry is malformed. But because a couple of name fields * in the 8.2 termcap file end with |\, we just have to assume * it's termcap syntax. */ _nc_syntax = SYN_TERMCAP; separator = ':'; } else if (_nc_syntax == SYN_TERMINFO) { /* throw away trailing /, *$/ */ for (--ptr; iswhite(*ptr) || *ptr == ','; ptr--) continue; ptr[1] = '\0'; } /* * This is the soonest we have the terminal name fetched. Set up * for following warning messages. */ ptr = strchr(buffer, '|'); if (ptr == (char *) NULL) ptr = buffer + strlen(buffer); ch = *ptr; *ptr = '\0'; _nc_set_type(buffer); *ptr = ch; /* * Compute the boundary between the aliases and the description * field for syntax-checking purposes. */ desc = strrchr(buffer, '|'); if (!silent && desc) { if (*desc == '\0') _nc_warning("empty longname field"); else if (strchr(desc, ' ') == (char *) NULL) _nc_warning("older tic versions may treat the description field as an alias"); } if (!desc) desc = buffer + strlen(buffer); /* * Whitespace in a name field other than the long name can confuse * rdist and some termcap tools. Slashes are a no-no. Other * special characters can be dangerous due to shell expansion. */ for (ptr = buffer; ptr < desc; ptr++) { if (isspace(CharOf(*ptr))) { if (!silent) _nc_warning("whitespace in name or alias field"); break; } else if (*ptr == '/') { if (!silent) _nc_warning("slashes aren't allowed in names or aliases"); break; } else if (strchr("$[]!*?", *ptr)) { if (!silent) _nc_warning("dubious character `%c' in name or alias field", *ptr); break; } } ptr = buffer; _nc_curr_token.tk_name = buffer; type = NAMES; } else { while ((ch = next_char()) != EOF) { if (!isalnum(ch)) { if (_nc_syntax == SYN_TERMINFO) { if (ch != '_') break; } else { /* allow ';' for "k;" */ if (ch != ';') break; } } *(ptr++) = ch; } *ptr++ = '\0'; switch (ch) { case ',': case ':': if (ch != separator) _nc_err_abort("Separator inconsistent with syntax"); _nc_curr_token.tk_name = buffer; type = BOOLEAN; break; case '@': if ((ch = next_char()) != separator && !silent) _nc_warning("Missing separator after `%s', have %s", buffer, unctrl((chtype) ch)); _nc_curr_token.tk_name = buffer; type = CANCEL; break; case '#': found = 0; while (isalnum(ch = next_char())) { numbuf[found++] = ch; if (found >= sizeof(numbuf) - 1) break; } numbuf[found] = '\0'; number = strtol(numbuf, &numchk, 0); if (!silent) { if (numchk == numbuf) _nc_warning("no value given for `%s'", buffer); if ((*numchk != '\0') || (ch != separator)) _nc_warning("Missing separator"); } _nc_curr_token.tk_name = buffer; _nc_curr_token.tk_valnumber = number; type = NUMBER; break; case '=': ch = _nc_trans_string(ptr, buffer + sizeof(buffer)); if (!silent && ch != separator) _nc_warning("Missing separator"); _nc_curr_token.tk_name = buffer; _nc_curr_token.tk_valstring = ptr; type = STRING; break; case EOF: type = EOF; break; default: /* just to get rid of the compiler warning */ type = UNDEF; if (!silent) _nc_warning("Illegal character - %s", unctrl((chtype) ch)); } } /* end else (first_column == FALSE) */ } /* end else (ch != EOF) */ end_of_token: #ifdef TRACE if (dot_flag == TRUE) DEBUG(8, ("Commented out ")); if (_nc_tracing >= DEBUG_LEVEL(7)) { switch (type) { case BOOLEAN: _tracef("Token: Boolean; name='%s'", _nc_curr_token.tk_name); break; case NUMBER: _tracef("Token: Number; name='%s', value=%d", _nc_curr_token.tk_name, _nc_curr_token.tk_valnumber); break; case STRING: _tracef("Token: String; name='%s', value=%s", _nc_curr_token.tk_name, _nc_visbuf(_nc_curr_token.tk_valstring)); break; case CANCEL: _tracef("Token: Cancel; name='%s'", _nc_curr_token.tk_name); break; case NAMES: _tracef("Token: Names; value='%s'", _nc_curr_token.tk_name); break; case EOF: _tracef("Token: End of file"); break; default: _nc_warning("Bad token type"); } } #endif if (dot_flag == TRUE) /* if commented out, use the next one */ type = _nc_get_token(silent); DEBUG(3, ("token: `%s', class %d", _nc_curr_token.tk_name != 0 ? _nc_curr_token.tk_name : "<null>", type)); return (type); }
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->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_cap_hash_table); to_ptr = _nc_find_entry(ap->to, _nc_info_hash_table); 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 (!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("``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"); } }
int _nc_parse_entry(struct entry *entryp, int literal, bool silent) { int token_type; struct name_table_entry const *entry_ptr; char *ptr, *base; token_type = _nc_get_token(); 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)); /* junk the 2-character termcap name, if present */ ptr = _nc_curr_token.tk_name; if (ptr[2] == '|') { ptr = _nc_curr_token.tk_name + 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", ptr - base, base); } } entryp->nuses = 0; for (token_type = _nc_get_token(); token_type != EOF && token_type != NAMES; token_type = _nc_get_token()) { if (strcmp(_nc_curr_token.tk_name, "use") == 0 || strcmp(_nc_curr_token.tk_name, "tc") == 0) { entryp->uses[entryp->nuses].name = _nc_save_str(_nc_curr_token.tk_valstring); entryp->uses[entryp->nuses].line = _nc_curr_line; entryp->nuses++; } 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) { 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); return (OK); }
_nc_trans_string(char *ptr, char *last) { int count = 0; int number = 0; int i, c; chtype ch, last_ch = '\0'; bool ignored = FALSE; bool long_warning = FALSE; while ((ch = c = next_char()) != (chtype) separator && c != EOF) { if (ptr == (last - 1)) break; if ((_nc_syntax == SYN_TERMCAP) && c == '\n') break; if (ch == '^' && last_ch != '%') { ch = c = next_char(); if (c == EOF) _nc_err_abort("Premature EOF"); if (!(is7bits(ch) && isprint(ch))) { _nc_warning("Illegal ^ character - %s", unctrl(ch)); } if (ch == '?') { *(ptr++) = '\177'; if (_nc_tracing) _nc_warning("Allow ^? as synonym for \\177"); } else { if ((ch &= 037) == 0) ch = 128; *(ptr++) = (char) (ch); } } else if (ch == '\\') { ch = c = next_char(); if (c == EOF) _nc_err_abort("Premature EOF"); if (ch >= '0' && ch <= '7') { number = ch - '0'; for (i = 0; i < 2; i++) { ch = c = next_char(); if (c == EOF) _nc_err_abort("Premature EOF"); if (c < '0' || c > '7') { if (isdigit(c)) { _nc_warning("Non-octal digit `%c' in \\ sequence", c); /* allow the digit; it'll do less harm */ } else { push_back((char) c); break; } } number = number * 8 + c - '0'; } if (number == 0) number = 0200; *(ptr++) = (char) number; } else { switch (c) { case 'E': case 'e': *(ptr++) = '\033'; break; case 'a': *(ptr++) = '\007'; break; case 'l': case 'n': *(ptr++) = '\n'; break; case 'r': *(ptr++) = '\r'; break; case 'b': *(ptr++) = '\010'; break; case 's': *(ptr++) = ' '; break; case 'f': *(ptr++) = '\014'; break; case 't': *(ptr++) = '\t'; break; case '\\': *(ptr++) = '\\'; break; case '^': *(ptr++) = '^'; break; case ',': *(ptr++) = ','; break; case ':': *(ptr++) = ':'; break; case '\n': continue; default: _nc_warning("Illegal character %s in \\ sequence", unctrl(ch)); *(ptr++) = (char) ch; } /* endswitch (ch) */ } /* endelse (ch < '0' || ch > '7') */ } /* end else if (ch == '\\') */ else if (ch == '\n' && (_nc_syntax == SYN_TERMINFO)) { /* newlines embedded in a terminfo string are ignored */ ignored = TRUE; } else { *(ptr++) = (char) ch; } if (!ignored) { last_ch = ch; count++; } ignored = FALSE; if (count > MAXCAPLEN && !long_warning) { _nc_warning("Very long string found. Missing separator?"); long_warning = TRUE; } } /* end while */ *ptr = '\0'; return (ch); }
static int next_char(void) { static char *result; static size_t allocated; int the_char; if (!yyin) { if (result != 0) { FreeAndNull(result); FreeAndNull(pushname); allocated = 0; } /* * An string with an embedded null will truncate the input. This is * intentional (we don't read binary files here). */ if (bufptr == 0 || *bufptr == '\0') return (EOF); if (*bufptr == '\n') { _nc_curr_line++; _nc_curr_col = 0; } else if (*bufptr == '\t') { _nc_curr_col = (_nc_curr_col | 7); } } else if (!bufptr || !*bufptr) { /* * In theory this could be recoded to do its I/O one character at a * time, saving the buffer space. In practice, this turns out to be * quite hard to get completely right. Try it and see. If you * succeed, don't forget to hack push_back() correspondingly. */ size_t used; size_t len; do { bufstart = 0; used = 0; do { if (used + (LEXBUFSIZ / 4) >= allocated) { allocated += (allocated + LEXBUFSIZ); result = typeRealloc(char, allocated, result); if (result == 0) return (EOF); bufstart = result; } if (used == 0) _nc_curr_file_pos = ftell(yyin); if (fgets(result + used, (int) (allocated - used), yyin) != 0) { bufstart = result; if (used == 0) { if (_nc_curr_line == 0 && IS_TIC_MAGIC(result)) { _nc_err_abort("This is a compiled terminal description, not a source"); } _nc_curr_line++; _nc_curr_col = 0; } } else { if (used != 0) strcat(result, "\n"); } if ((bufptr = bufstart) != 0) { used = strlen(bufptr); while (iswhite(*bufptr)) { if (*bufptr == '\t') { _nc_curr_col = (_nc_curr_col | 7) + 1; } else { _nc_curr_col++; } bufptr++; } /* * Treat a trailing <cr><lf> the same as a <newline> so we * can read files on OS/2, etc. */ if ((len = strlen(bufptr)) > 1) { if (bufptr[len - 1] == '\n' && bufptr[len - 2] == '\r') { len--; bufptr[len - 1] = '\n'; bufptr[len] = '\0'; } } } else { return (EOF); } } while (bufptr[len - 1] != '\n'); /* complete a line */ } while (result[0] == '#'); /* ignore comments */