libncurses: Import https://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.5.tar.gz changes

Change-Id: I3433d30ca01359fd2e3623ede96b531f0b39cbfa
Signed-off-by: micky387 <mickaelsaibi@free.fr>
diff --git a/ncurses/tinfo/comp_parse.c b/ncurses/tinfo/comp_parse.c
index 82a61a5..dec4b92 100644
--- a/ncurses/tinfo/comp_parse.c
+++ b/ncurses/tinfo/comp_parse.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2023,2024 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,23 +48,22 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: comp_parse.c,v 1.90 2013/08/31 15:22:31 tom Exp $")
+MODULE_ID("$Id: comp_parse.c,v 1.134 2024/02/10 15:52:11 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 *, int);
+static void fixup_acsc(TERMTYPE2 *, int);
 
 static void
 enqueue(ENTRY * ep)
 /* add an entry to the in-core list */
 {
-    ENTRY *newp = _nc_copy_entry(ep);
+    ENTRY *newp;
 
+    DEBUG(2, (T_CALLED("enqueue(ep=%p)"), (void *) ep));
+
+    newp = _nc_copy_entry(ep);
     if (newp == 0)
 	_nc_err_abort(MSG_NO_MEMORY);
 
@@ -73,8 +73,11 @@
     newp->next = 0;
     if (newp->last)
 	newp->last->next = newp;
+    DEBUG(2, (T_RETURN("")));
 }
 
+#define NAMEBUFFER_SIZE (MAX_NAME_SIZE + 2)
+
 static char *
 force_bar(char *dst, char *src)
 {
@@ -82,8 +85,8 @@
 	size_t len = strlen(src);
 	if (len > MAX_NAME_SIZE)
 	    len = MAX_NAME_SIZE;
-	(void) strncpy(dst, src, len);
-	_nc_STRCPY(dst + len, "|", MAX_NAME_SIZE);
+	_nc_STRNCPY(dst, src, MAX_NAME_SIZE);
+	_nc_STRCPY(dst + len, "|", NAMEBUFFER_SIZE - len);
 	src = dst;
     }
     return src;
@@ -107,8 +110,8 @@
 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);
@@ -182,11 +185,11 @@
 			++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);
 		}
-		removed = TRUE;
 		break;
 	    }
 	}
@@ -218,6 +221,12 @@
     bool oldsuppress = _nc_suppress_warnings;
     int immediate = 0;
 
+    DEBUG(2,
+	  (T_CALLED("_nc_read_entry_source("
+		    "file=%p, buf=%p, literal=%d, silent=%d, hook=%#"
+		    PRIxPTR ")"),
+	   (void *) fp, buf, literal, silent, CASTxPTR(hook)));
+
     if (silent)
 	_nc_suppress_warnings = TRUE;	/* shut the lexer up, too */
 
@@ -245,38 +254,159 @@
 	    FreeIfNeeded(thisentry.tterm.Booleans);
 	    FreeIfNeeded(thisentry.tterm.Numbers);
 	    FreeIfNeeded(thisentry.tterm.Strings);
+	    FreeIfNeeded(thisentry.tterm.str_table);
 #if NCURSES_XNAMES
 	    FreeIfNeeded(thisentry.tterm.ext_Names);
+	    FreeIfNeeded(thisentry.tterm.ext_str_table);
 #endif
 	}
     }
 
     if (_nc_tail) {
 	/* set up the head pointer */
-	for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last)
-	    continue;
+	for (_nc_head = _nc_tail; _nc_head->last; _nc_head = _nc_head->last) {
+	    /* EMPTY */ ;
+	}
 
-	DEBUG(1, ("head = %s", _nc_head->tterm.term_names));
-	DEBUG(1, ("tail = %s", _nc_tail->tterm.term_names));
+	DEBUG(2, ("head = %s", _nc_head->tterm.term_names));
+	DEBUG(2, ("tail = %s", _nc_tail->tterm.term_names));
     }
 #ifdef TRACE
     else if (!immediate)
-	DEBUG(1, ("no entries parsed"));
+	DEBUG(2, ("no entries parsed"));
 #endif
 
     _nc_suppress_warnings = oldsuppress;
+    DEBUG(2, (T_RETURN("")));
 }
 
+#if 0 && 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 = ((p->Strings[STRCOUNT + which] != CANCELLED_STRING)
+			  ? STRING
+			  : CANCEL);
+	    } 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)) {
+		    _nc_warning("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 {
+		    _nc_warning("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));
+		}
+		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;
-    int unresolved, total_unresolved, multiples;
+    unsigned i, j;
+    int total_unresolved, multiples;
 
-    DEBUG(2, ("RESOLUTION BEGINNING"));
+    DEBUG(2, (T_CALLED("_nc_resolve_uses2")));
 
     /*
      * Check for multiple occurrences of the same name.
@@ -285,7 +415,7 @@
     for_entry_list(qp) {
 	int matchcount = 0;
 
-	for_entry_list(rp) {
+	for_entry_list2(rp, qp->next) {
 	    if (qp > rp
 		&& check_collisions(qp->tterm.term_names,
 				    rp->tterm.term_names,
@@ -301,8 +431,10 @@
 	    }
 	}
     }
-    if (multiples > 0)
+    if (multiples > 0) {
+	DEBUG(2, (T_RETURN("false")));
 	return (FALSE);
+    }
 
     DEBUG(2, ("NO MULTIPLE NAME OCCURRENCES"));
 
@@ -312,13 +444,15 @@
     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;
 
+	    if (lookfor == 0)
+		continue;
+
 	    foundit = FALSE;
 
 	    _nc_set_type(child);
@@ -327,21 +461,31 @@
 	    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));
+		    DEBUG(2, ("%s: resolving use=%s %p (in core)",
+			      child, lookfor, lookfor));
 
 		    qp->uses[i].link = rp;
 		    foundit = TRUE;
+
+		    /* verify that there are no earlier uses */
+		    for (j = 0; j < i; ++j) {
+			if (qp->uses[j].link != NULL
+			    && !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));
 
@@ -353,12 +497,21 @@
 
 		    qp->uses[i].link = rp;
 		    foundit = TRUE;
+
+		    /* verify that there are no earlier uses */
+		    for (j = 0; j < i; ++j) {
+			if (qp->uses[j].link != NULL
+			    && !strcmp(qp->uses[j].link->tterm.term_names,
+				       rp->tterm.term_names)) {
+			    _nc_warning("duplicate use=%s", lookfor);
+			    break;
+			}
+		    }
 		}
 	    }
 
 	    /* no good, mark this one unresolvable and complain */
 	    if (!foundit) {
-		unresolved++;
 		total_unresolved++;
 
 		_nc_curr_line = (int) lookline;
@@ -370,6 +523,7 @@
     if (total_unresolved) {
 	/* free entries read in off disk */
 	_nc_free_entries(lastread);
+	DEBUG(2, (T_RETURN("false")));
 	return (FALSE);
     }
 
@@ -382,25 +536,28 @@
      */
     if (fullresolve) {
 	do {
-	    TERMTYPE merged;
+	    ENTRY merged;
 
 	    keepgoing = FALSE;
 
 	    for_entry_list(qp) {
 		if (qp->nuses > 0) {
-		    DEBUG(2, ("%s: attempting merge",
-			      _nc_first_name(qp->tterm.term_names)));
+		    DEBUG(2, ("%s: attempting merge of %d entries",
+			      _nc_first_name(qp->tterm.term_names),
+			      qp->nuses));
 		    /*
 		     * 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) {
+		    for (i = 0; i < qp->nuses; i++) {
+			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;
 			}
+		    }
 
 		    /*
 		     * First, make sure there is no garbage in the
@@ -408,20 +565,24 @@
 		     * 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--)
-			_nc_merge_entry(&merged,
-					&qp->uses[qp->nuses - 1].link->tterm);
+		    for (; qp->nuses; qp->nuses--) {
+			int n = (int) (qp->nuses - 1);
+			validate_merge(&merged, qp->uses[n].link);
+			_nc_merge_entry(&merged, qp->uses[n].link);
+			free(qp->uses[n].name);
+		    }
 
 		    /*
 		     * 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.
@@ -429,10 +590,12 @@
 		    FreeIfNeeded(qp->tterm.Booleans);
 		    FreeIfNeeded(qp->tterm.Numbers);
 		    FreeIfNeeded(qp->tterm.Strings);
+		    FreeIfNeeded(qp->tterm.str_table);
 #if NCURSES_XNAMES
 		    FreeIfNeeded(qp->tterm.ext_Names);
+		    FreeIfNeeded(qp->tterm.ext_str_table);
 #endif
-		    qp->tterm = merged;
+		    qp->tterm = merged.tterm;
 		    _nc_wrap_entry(qp, TRUE);
 
 		    /*
@@ -450,63 +613,57 @@
 	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 = (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;
+
 		/*
-		 * tic overrides this function pointer to provide more verbose
-		 * checking.
+		 * Setup so that tic can use ordinary terminfo interface to
+		 * obtain capability information.
 		 */
-		if (_nc_check_termtype2 != sanity_check2) {
-		    SCREEN *save_SP = SP;
-		    SCREEN fake_sp;
-		    TERMINAL fake_tm;
-		    TERMINAL *save_tm = cur_term;
+		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);
 
-		    /*
-		     * 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;
-		    fake_tm.type = qp->tterm;
-		    _nc_set_screen(&fake_sp);
-		    set_curterm(&fake_tm);
+		_nc_check_termtype2(&qp->tterm, literal);
 
-		    _nc_check_termtype2(&qp->tterm, literal);
+		/*
+		 * Checking calls tparm, which can allocate memory.  Fix leaks.
+		 */
+#define TPS(name) fake_tm.tparm_state.name
+		FreeAndNull(TPS(out_buff));
+		FreeAndNull(TPS(fmt_buff));
+#undef TPS
 
-		    _nc_set_screen(save_SP);
-		    set_curterm(save_tm);
-		} else {
-		    fixup_acsc(&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"));
+    }
 
+    DEBUG(2, (T_RETURN("true")));
     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 ---
@@ -517,18 +674,18 @@
 #define CUR tp->
 
 static void
-fixup_acsc(TERMTYPE *tp, int 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 */
@@ -547,7 +704,9 @@
 #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
@@ -578,23 +737,18 @@
     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_leaks_tic()")));
+    _nc_globals.leak_checking = TRUE;
     _nc_alloc_entry_leaks();
     _nc_captoinfo_leaks();
     _nc_comp_scan_leaks();
@@ -603,12 +757,14 @@
     _nc_codes_leaks();
 #endif
     _nc_tic_expand(0, FALSE, 0);
+    T((T_RETURN("")));
 }
 
 NCURSES_EXPORT(void)
 _nc_free_tic(int code)
 {
+    T((T_CALLED("_nc_free_tic(%d)"), code));
     _nc_leaks_tic();
-    _nc_free_tinfo(code);
+    exit_terminfo(code);
 }
 #endif