_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")); }
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); }