X-Git-Url: http://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Ftinfo%2Fcomp_parse.c;h=e47625b3dbe1735ff8cd2044bd804d6202ad9a1d;hp=649731346395cb7a0b6346f141bc195c33c51e25;hb=3934889cc6b0f45cf218c88df7f35634d250c9e5;hpb=8e25fff6a5f576b6dc35eb02b9783fa58680d07b diff --git a/ncurses/tinfo/comp_parse.c b/ncurses/tinfo/comp_parse.c index 64973134..e47625b3 100644 --- a/ncurses/tinfo/comp_parse.c +++ b/ncurses/tinfo/comp_parse.c @@ -1,5 +1,6 @@ /**************************************************************************** - * Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. * + * Copyright 2018-2020,2021 Thomas E. Dickey * + * Copyright 1998-2016,2017 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -47,16 +48,12 @@ #include -MODULE_ID("$Id: comp_parse.c,v 1.74 2011/07/25 22:46:47 tom Exp $") +MODULE_ID("$Id: comp_parse.c,v 1.112 2021/02/27 21:01:21 tom Exp $") -static void sanity_check2(TERMTYPE *, bool); -NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype2) (TERMTYPE *, bool) = sanity_check2; +static void sanity_check2(TERMTYPE2 *, bool); +NCURSES_IMPEXP void (NCURSES_API *_nc_check_termtype2) (TERMTYPE2 *, bool) = sanity_check2; -/* obsolete: 20040705 */ -static void sanity_check(TERMTYPE *); -NCURSES_IMPEXP void NCURSES_API(*_nc_check_termtype) (TERMTYPE *) = sanity_check; - -static void fixup_acsc(TERMTYPE *, bool); +static void fixup_acsc(TERMTYPE2 *, int); static void enqueue(ENTRY * ep) @@ -75,6 +72,8 @@ enqueue(ENTRY * ep) newp->last->next = newp; } +#define NAMEBUFFER_SIZE (MAX_NAME_SIZE + 2) + static char * force_bar(char *dst, char *src) { @@ -82,34 +81,126 @@ force_bar(char *dst, char *src) size_t len = strlen(src); if (len > MAX_NAME_SIZE) len = MAX_NAME_SIZE; - (void) strncpy(dst, src, len); - (void) strcpy(dst + len, "|"); + _nc_STRNCPY(dst, src, MAX_NAME_SIZE); + _nc_STRCPY(dst + len, "|", NAMEBUFFER_SIZE - len); src = dst; } return src; } #define ForceBar(dst, src) ((strchr(src, '|') == 0) ? force_bar(dst, src) : src) -NCURSES_EXPORT(bool) -_nc_entry_match(char *n1, char *n2) -/* do any of the aliases in a pair of terminal names match? */ +#if NCURSES_USE_TERMCAP && NCURSES_XNAMES +static char * +skip_index(char *name) +{ + char *bar = strchr(name, '|'); + + if (bar != 0 && (bar - name) == 2) + name = bar + 1; + + return name; +} +#endif + +static bool +check_collisions(char *n1, char *n2, int counter) { char *pstart, *qstart, *pend, *qend; - char nc1[MAX_NAME_SIZE + 2]; - char nc2[MAX_NAME_SIZE + 2]; + char nc1[NAMEBUFFER_SIZE]; + char nc2[NAMEBUFFER_SIZE]; n1 = ForceBar(nc1, n1); n2 = ForceBar(nc2, n2); - for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) - for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) +#if NCURSES_USE_TERMCAP && NCURSES_XNAMES + if ((_nc_syntax == SYN_TERMCAP) && _nc_user_definable) { + n1 = skip_index(n1); + n2 = skip_index(n2); + } +#endif + + for (pstart = n1; (pend = strchr(pstart, '|')); pstart = pend + 1) { + for (qstart = n2; (qend = strchr(qstart, '|')); qstart = qend + 1) { if ((pend - pstart == qend - qstart) - && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) + && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) { + if (counter > 0) + (void) fprintf(stderr, "Name collision '%.*s' between\n", + (int) (pend - pstart), pstart); return (TRUE); + } + } + } return (FALSE); } +static char * +next_name(char *name) +{ + if (*name != '\0') + ++name; + return name; +} + +static char * +name_ending(char *name) +{ + if (*name == '\0') { + name = 0; + } else { + while (*name != '\0' && *name != '|') + ++name; + } + return name; +} + +/* + * Essentially, find the conflict reported in check_collisions() and remove + * it from the second name, unless that happens to be the last alias. + */ +static bool +remove_collision(char *n1, char *n2) +{ + char *p2 = n2; + char *pstart, *qstart, *pend, *qend; + bool removed = FALSE; + +#if NCURSES_USE_TERMCAP && NCURSES_XNAMES + if ((_nc_syntax == SYN_TERMCAP) && _nc_user_definable) { + n1 = skip_index(n1); + p2 = n2 = skip_index(n2); + } +#endif + + for (pstart = n1; (pend = name_ending(pstart)); pstart = next_name(pend)) { + for (qstart = n2; (qend = name_ending(qstart)); qstart = next_name(qend)) { + if ((pend - pstart == qend - qstart) + && memcmp(pstart, qstart, (size_t) (pend - pstart)) == 0) { + if (qstart != p2 || *qend == '|') { + if (*qend == '|') + ++qend; + while ((*qstart++ = *qend++) != '\0') ; + fprintf(stderr, "...now\t%s\n", p2); + removed = TRUE; + } else { + fprintf(stderr, "Cannot remove alias '%.*s'\n", + (int) (qend - qstart), qstart); + } + break; + } + } + } + + return removed; +} + +/* do any of the aliases in a pair of terminal names match? */ +NCURSES_EXPORT(bool) +_nc_entry_match(char *n1, char *n2) +{ + return check_collisions(n1, n2, 0); +} + /**************************************************************************** * * Entry compiler and resolution logic @@ -175,13 +266,133 @@ _nc_read_entry_source(FILE *fp, char *buf, _nc_suppress_warnings = oldsuppress; } +#if NCURSES_XNAMES +static unsigned +find_capname(TERMTYPE2 *p, const char *name) +{ + unsigned num_names = NUM_EXT_NAMES(p); + unsigned n; + if (name != 0) { + for (n = 0; n < num_names; ++n) { + if (!strcmp(p->ext_Names[n], name)) + break; + } + } else { + n = num_names + 1; + } + return n; +} + +static int +extended_captype(TERMTYPE2 *p, unsigned which) +{ + int result = UNDEF; + unsigned limit = 0; + limit += p->ext_Booleans; + if (limit != 0 && which < limit) { + result = BOOLEAN; + } else { + limit += p->ext_Numbers; + if (limit != 0 && which < limit) { + result = NUMBER; + } else { + limit += p->ext_Strings; + if (limit != 0 && which < limit) { + result = STRING; + } else if (which >= limit) { + result = CANCEL; + } + } + } + return result; +} + +static const char * +name_of_captype(int which) +{ + const char *result = "?"; + switch (which) { + case BOOLEAN: + result = "boolean"; + break; + case NUMBER: + result = "number"; + break; + case STRING: + result = "string"; + break; + } + return result; +} + +#define valid_TERMTYPE2(p) \ + ((p) != 0 && \ + (p)->term_names != 0 && \ + (p)->ext_Names != 0) + +/* + * Disallow changing the type of an extended capability when doing a "use" + * if one or the other is a string. + */ +static int +invalid_merge(TERMTYPE2 *to, TERMTYPE2 *from) +{ + int rc = FALSE; + if (valid_TERMTYPE2(to) + && valid_TERMTYPE2(from)) { + char *to_name = _nc_first_name(to->term_names); + char *from_name = strdup(_nc_first_name(from->term_names)); + unsigned num_names = NUM_EXT_NAMES(from); + unsigned n; + + for (n = 0; n < num_names; ++n) { + const char *capname = from->ext_Names[n]; + int tt = extended_captype(to, find_capname(to, capname)); + int tf = extended_captype(from, n); + + if (tt <= STRING + && tf <= STRING + && (tt == STRING) != (tf == STRING)) { + if (from_name != 0 && strcmp(to_name, from_name)) { + DEBUG(2, + ("merge of %s to %s changes type of %s from %s to %s", + from_name, + to_name, + from->ext_Names[n], + name_of_captype(tf), + name_of_captype(tt))); + } else { + DEBUG(2, ("merge of %s changes type of %s from %s to %s", + to_name, + from->ext_Names[n], + name_of_captype(tf), + name_of_captype(tt))); + } + _nc_warning("merge changes type of %s from %s to %s", + from->ext_Names[n], + name_of_captype(tf), + name_of_captype(tt)); + rc = TRUE; + } + } + free(from_name); + } + return rc; +} +#define validate_merge(p, q) \ + if (invalid_merge(&((p)->tterm), &((q)->tterm))) \ + return FALSE +#else +#define validate_merge(p, q) /* nothing */ +#endif + NCURSES_EXPORT(int) _nc_resolve_uses2(bool fullresolve, bool literal) /* try to resolve all use capabilities */ { ENTRY *qp, *rp, *lastread = 0; bool keepgoing; - unsigned i; + unsigned i, j; int unresolved, total_unresolved, multiples; DEBUG(2, ("RESOLUTION BEGINNING")); @@ -195,19 +406,19 @@ _nc_resolve_uses2(bool fullresolve, bool literal) 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++; + && check_collisions(qp->tterm.term_names, + rp->tterm.term_names, + matchcount + 1)) { + if (!matchcount++) { + (void) fprintf(stderr, "\t%s\n", rp->tterm.term_names); + } + (void) fprintf(stderr, "and\t%s\n", qp->tterm.term_names); + if (!remove_collision(rp->tterm.term_names, + 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); @@ -227,6 +438,9 @@ _nc_resolve_uses2(bool fullresolve, bool literal) char *lookfor = qp->uses[i].name; long lookline = qp->uses[i].line; + if (lookfor == 0) + continue; + foundit = FALSE; _nc_set_type(child); @@ -240,22 +454,29 @@ _nc_resolve_uses2(bool fullresolve, bool literal) qp->uses[i].link = rp; foundit = TRUE; + + /* verify that there are no earlier uses */ + for (j = 0; j < i; ++j) { + if (!strcmp(qp->uses[j].link->tterm.term_names, + rp->tterm.term_names)) { + _nc_warning("duplicate use=%s", lookfor); + break; + } + } } } /* if that didn't work, try to merge in a compiled entry */ if (!foundit) { - TERMTYPE thisterm; + TERMTYPE2 thisterm; char filename[PATH_MAX]; memset(&thisterm, 0, sizeof(thisterm)); - if (_nc_read_entry(lookfor, filename, &thisterm) == 1) { + if (_nc_read_entry2(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); + TYPE_MALLOC(ENTRY, 1, rp); rp->tterm = thisterm; rp->nuses = 0; rp->next = lastread; @@ -263,6 +484,15 @@ _nc_resolve_uses2(bool fullresolve, bool literal) qp->uses[i].link = rp; foundit = TRUE; + + /* verify that there are no earlier uses */ + for (j = 0; j < i; ++j) { + if (!strcmp(qp->uses[j].link->tterm.term_names, + rp->tterm.term_names)) { + _nc_warning("duplicate use=%s", lookfor); + break; + } + } } } @@ -292,7 +522,7 @@ _nc_resolve_uses2(bool fullresolve, bool literal) */ if (fullresolve) { do { - TERMTYPE merged; + ENTRY merged; keepgoing = FALSE; @@ -306,7 +536,8 @@ _nc_resolve_uses2(bool fullresolve, bool literal) * subsequent pass. */ for (i = 0; i < qp->nuses; i++) - if (qp->uses[i].link->nuses) { + if (qp->uses[i].link + && qp->uses[i].link->nuses) { DEBUG(2, ("%s: use entry %d unresolved", _nc_first_name(qp->tterm.term_names), i)); goto incomplete; @@ -318,20 +549,24 @@ _nc_resolve_uses2(bool fullresolve, bool literal) * the merged entry the name field and string * table pointer. */ - _nc_copy_termtype(&merged, &(qp->tterm)); + _nc_copy_termtype2(&(merged.tterm), &(qp->tterm)); /* * Now merge in each use entry in the proper * (reverse) order. */ - for (; qp->nuses; qp->nuses--) + for (; qp->nuses; qp->nuses--) { + validate_merge(&merged, + qp->uses[qp->nuses - 1].link); _nc_merge_entry(&merged, - &qp->uses[qp->nuses - 1].link->tterm); + qp->uses[qp->nuses - 1].link); + } /* * Now merge in the original entry. */ - _nc_merge_entry(&merged, &qp->tterm); + validate_merge(&merged, qp); + _nc_merge_entry(&merged, qp); /* * Replace the original entry with the merged one. @@ -342,7 +577,7 @@ _nc_resolve_uses2(bool fullresolve, bool literal) #if NCURSES_XNAMES FreeIfNeeded(qp->tterm.ext_Names); #endif - qp->tterm = merged; + qp->tterm = merged.tterm; _nc_wrap_entry(qp, TRUE); /* @@ -369,27 +604,46 @@ _nc_resolve_uses2(bool fullresolve, bool literal) DEBUG(2, ("RESOLUTION FINISHED")); - if (fullresolve) - if (_nc_check_termtype != 0) { - _nc_curr_col = -1; - for_entry_list(qp) { - _nc_curr_line = (int) qp->startline; - _nc_set_type(_nc_first_name(qp->tterm.term_names)); + if (fullresolve) { + _nc_curr_col = -1; + for_entry_list(qp) { + _nc_curr_line = (int) qp->startline; + _nc_set_type(_nc_first_name(qp->tterm.term_names)); + /* + * tic overrides this function pointer to provide more verbose + * checking. + */ + if (_nc_check_termtype2 != sanity_check2) { + SCREEN *save_SP = SP; + SCREEN fake_sp; + TERMINAL fake_tm; + TERMINAL *save_tm = cur_term; + + /* + * Setup so that tic can use ordinary terminfo interface to + * obtain capability information. + */ + memset(&fake_sp, 0, sizeof(fake_sp)); + memset(&fake_tm, 0, sizeof(fake_tm)); + fake_sp._term = &fake_tm; + TerminalType(&fake_tm) = qp->tterm; + _nc_set_screen(&fake_sp); + set_curterm(&fake_tm); + + _nc_check_termtype2(&qp->tterm, literal); + + _nc_set_screen(save_SP); + set_curterm(save_tm); + } else { fixup_acsc(&qp->tterm, literal); } - DEBUG(2, ("SANITY CHECK FINISHED")); } + DEBUG(2, ("SANITY CHECK FINISHED")); + } return (TRUE); } -/* obsolete: 20040705 */ -NCURSES_EXPORT(int) -_nc_resolve_uses(bool fullresolve) -{ - return _nc_resolve_uses2(fullresolve, FALSE); -} - /* * This bit of legerdemain turns all the terminfo variable names into * references to locations in the arrays Booleans, Numbers, and Strings --- @@ -400,18 +654,18 @@ _nc_resolve_uses(bool fullresolve) #define CUR tp-> static void -fixup_acsc(TERMTYPE *tp, bool literal) +fixup_acsc(TERMTYPE2 *tp, int literal) { if (!literal) { - if (acs_chars == 0 - && enter_alt_charset_mode != 0 - && exit_alt_charset_mode != 0) + if (acs_chars == ABSENT_STRING + && PRESENT(enter_alt_charset_mode) + && PRESENT(exit_alt_charset_mode)) acs_chars = strdup(VT_ACSC); } } static void -sanity_check2(TERMTYPE *tp, bool literal) +sanity_check2(TERMTYPE2 *tp, bool literal) { if (!PRESENT(exit_attribute_mode)) { #ifdef __UNUSED__ /* this casts too wide a net */ @@ -430,6 +684,9 @@ sanity_check2(TERMTYPE *tp, bool literal) #endif /* __UNUSED__ */ PAIRED(enter_standout_mode, exit_standout_mode); PAIRED(enter_underline_mode, exit_underline_mode); +#if defined(enter_italics_mode) && defined(exit_italics_mode) + PAIRED(enter_italics_mode, exit_italics_mode); +#endif } /* we do this check/fix in postprocess_termcap(), but some packagers @@ -460,23 +717,18 @@ sanity_check2(TERMTYPE *tp, bool literal) PAIRED(enter_xon_mode, exit_xon_mode); PAIRED(enter_am_mode, exit_am_mode); ANDMISSING(label_off, label_on); -#ifdef remove_clock +#if defined(display_clock) && defined(remove_clock) PAIRED(display_clock, remove_clock); #endif ANDMISSING(set_color_pair, initialize_pair); } -/* obsolete: 20040705 */ -static void -sanity_check(TERMTYPE *tp) -{ - sanity_check2(tp, FALSE); -} - #if NO_LEAKS NCURSES_EXPORT(void) _nc_leaks_tic(void) { + T((T_CALLED("_nc_free_tic()"))); + _nc_globals.leak_checking = TRUE; _nc_alloc_entry_leaks(); _nc_captoinfo_leaks(); _nc_comp_scan_leaks(); @@ -491,6 +743,6 @@ NCURSES_EXPORT(void) _nc_free_tic(int code) { _nc_leaks_tic(); - _nc_free_tinfo(code); + exit_terminfo(code); } #endif