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/MKcaptab.awk b/ncurses/tinfo/MKcaptab.awk
index 56d3d17..ee4e2e9 100644
--- a/ncurses/tinfo/MKcaptab.awk
+++ b/ncurses/tinfo/MKcaptab.awk
@@ -1,5 +1,6 @@
 ##############################################################################
-# Copyright (c) 1998-2006,2007 Free Software Foundation, Inc.                #
+# Copyright 2020 Thomas E. Dickey                                            #
+# Copyright 1998-2006,2007 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 "Software"), #
@@ -25,7 +26,7 @@
 # use or other dealings in this Software without prior written               #
 # authorization.                                                             #
 ##############################################################################
-# $Id: MKcaptab.awk,v 1.20 2007/08/12 00:26:15 tom Exp $
+# $Id: MKcaptab.awk,v 1.21 2020/02/02 23:34:34 tom Exp $
 function add_string(text) {
     if (text != "IGNORE") {
 	offsets[num_strings] = offset;
diff --git a/ncurses/tinfo/MKcaptab.sh b/ncurses/tinfo/MKcaptab.sh
index 20c94a6..5f41350 100755
--- a/ncurses/tinfo/MKcaptab.sh
+++ b/ncurses/tinfo/MKcaptab.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 ##############################################################################
-# Copyright (c) 2007-2010,2011 Free Software Foundation, Inc.                #
+# Copyright 2019-2020,2023 Thomas E. Dickey                                  #
+# Copyright 2007-2010,2011 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 "Software"), #
@@ -26,11 +27,28 @@
 # use or other dealings in this Software without prior written               #
 # authorization.                                                             #
 ##############################################################################
-# $Id: MKcaptab.sh,v 1.14 2011/10/22 16:34:50 tom Exp $
-AWK=${1-awk}
-OPT1=${2-0}
-OPT2=${3-tinfo/MKcaptab.awk}
-DATA=${4-../include/Caps}
+# $Id: MKcaptab.sh,v 1.20 2023/04/22 15:12:57 tom Exp $
+
+if test $# != 0
+then
+	AWK="$1"; shift 1
+else
+	AWK=awk
+fi
+
+if test $# != 0
+then
+	OPT1="$1"; shift 1
+else
+	OPT1="-0"
+fi
+
+if test $# != 0
+then
+	OPT2="$1"; shift 1
+else
+	OPT2="tinfo/MKcaptab.awk"
+fi
 
 cat <<EOF
 /*
@@ -50,16 +68,18 @@
 #include <tic.h>
 #include <hashsize.h>
 
+/* *INDENT-OFF* */
 EOF
 
-./make_hash 1 info $OPT1 <$DATA
-./make_hash 3 cap  $OPT1 <$DATA
+cat "$@" |./make_hash 1 info $OPT1
+cat "$@" |./make_hash 3 cap  $OPT1
 
-$AWK -f $OPT2 bigstrings=$OPT1 tablename=capalias <$DATA
+cat "$@" |$AWK -f $OPT2 bigstrings=$OPT1 tablename=capalias
 
-$AWK -f $OPT2 bigstrings=$OPT1 tablename=infoalias <$DATA
+cat "$@" |$AWK -f $OPT2 bigstrings=$OPT1 tablename=infoalias
 
 cat <<EOF
+/* *INDENT-ON* */
 
 #if $OPT1
 static void
@@ -70,7 +90,7 @@
 
 static const struct name_table_entry *
 _nc_build_names(struct name_table_entry **actual,
-		const name_table_data *source,
+		const name_table_data * source,
 		const char *strings)
 {
     if (*actual == 0) {
@@ -97,7 +117,7 @@
 
 static const struct alias *
 _nc_build_alias(struct alias **actual,
-		const alias_table_data *source,
+		const alias_table_data * source,
 		const char *strings,
 		size_t tablesize)
 {
@@ -127,20 +147,22 @@
 #define build_alias(root) _nc_ ## root ## alias_table
 #endif
 
-NCURSES_EXPORT(const struct name_table_entry *) _nc_get_table (bool termcap)
+NCURSES_EXPORT(const struct name_table_entry *)
+_nc_get_table(bool termcap)
 {
-    return termcap ? build_names(cap) : build_names(info) ;
+    return termcap ? build_names(cap) : build_names(info);
 }
 
-/* entrypoint used by tack (do not alter) */
-NCURSES_EXPORT(const HashValue *) _nc_get_hash_table (bool termcap)
+NCURSES_EXPORT(const HashValue *)
+_nc_get_hash_table(bool termcap)
 {
-    return termcap ? _nc_cap_hash_table: _nc_info_hash_table ;
+    return termcap ? _nc_cap_hash_table : _nc_info_hash_table;
 }
 
-NCURSES_EXPORT(const struct alias *) _nc_get_alias_table (bool termcap)
+NCURSES_EXPORT(const struct alias *)
+_nc_get_alias_table(bool termcap)
 {
-    return termcap ? build_alias(cap) : build_alias(info) ;
+    return termcap ? build_alias(cap) : build_alias(info);
 }
 
 static HashValue
@@ -150,7 +172,7 @@
 
     DEBUG(9, ("hashing %s", string));
     while (*string) {
-	sum += (long) (*string + (*(string + 1) << 8));
+	sum += (long) (UChar(*string) + (UChar(*(string + 1)) << 8));
 	string++;
     }
 
@@ -187,18 +209,21 @@
     return !strcmp(a, b);
 }
 
-static const HashData hash_data[2] = {
-    { HASHTABSIZE, _nc_info_hash_table, info_hash, compare_info_names },
-    { HASHTABSIZE, _nc_cap_hash_table, tcap_hash, compare_tcap_names }
+static const HashData hash_data[2] =
+{
+    {HASHTABSIZE, _nc_info_hash_table, info_hash, compare_info_names},
+    {HASHTABSIZE, _nc_cap_hash_table, tcap_hash, compare_tcap_names}
 };
 
-NCURSES_EXPORT(const HashData *) _nc_get_hash_info (bool termcap)
+NCURSES_EXPORT(const HashData *)
+_nc_get_hash_info(bool termcap)
 {
     return &hash_data[(termcap != FALSE)];
 }
 
 #if NO_LEAKS
-NCURSES_EXPORT(void) _nc_comp_captab_leaks(void)
+NCURSES_EXPORT(void)
+_nc_comp_captab_leaks(void)
 {
 #if $OPT1
     FreeIfNeeded(_nc_cap_table);
diff --git a/ncurses/tinfo/MKcodes.awk b/ncurses/tinfo/MKcodes.awk
index 97e5131..48f4800 100644
--- a/ncurses/tinfo/MKcodes.awk
+++ b/ncurses/tinfo/MKcodes.awk
@@ -1,5 +1,6 @@
 ##############################################################################
-# Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.                #
+# Copyright 2019,2020 Thomas E. Dickey                                       #
+# Copyright 2007-2009,2010 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 "Software"), #
@@ -25,7 +26,7 @@
 # use or other dealings in this Software without prior written               #
 # authorization.                                                             #
 ##############################################################################
-# $Id: MKcodes.awk,v 1.9 2010/01/23 17:57:43 tom Exp $
+# $Id: MKcodes.awk,v 1.11 2020/02/02 23:34:34 tom Exp $
 function large_item(value) {
 	result = sprintf("%d,", offset);
 	offset = offset + length(value) + 1;
@@ -79,7 +80,9 @@
 	}
 
 $1 ~ /^#/		{next;}
+$1 ~ /^(cap|info)alias/	{next;}
 
+$1 == "userdef"		{next;}
 $1 == "SKIPWARN"	{next;}
 
 $3 == "bool"	{
diff --git a/ncurses/tinfo/MKfallback.sh b/ncurses/tinfo/MKfallback.sh
index 11f1d2e..02b36ed 100755
--- a/ncurses/tinfo/MKfallback.sh
+++ b/ncurses/tinfo/MKfallback.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 ##############################################################################
-# Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.                #
+# Copyright 2020,2023 Thomas E. Dickey                                       #
+# Copyright 1998-2019,2020 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 "Software"), #
@@ -26,7 +27,7 @@
 # use or other dealings in this Software without prior written               #
 # authorization.                                                             #
 ##############################################################################
-# $Id: MKfallback.sh,v 1.15 2010/08/07 20:32:34 tom Exp $
+# $Id: MKfallback.sh,v 1.26 2023/04/22 15:12:57 tom Exp $
 #
 # MKfallback.sh -- create fallback table for entry reads
 #
@@ -43,9 +44,14 @@
 shift
 
 tic_path=$1
+test -z "$tic_path" && tic_path=tic
 shift
 
-case $tic_path in #(vi
+infocmp_path=$1
+test -z "$infocmp_path" && infocmp_path=infocmp
+shift
+
+case "$tic_path" in #(vi
 /*)
 	tic_head=`echo "$tic_path" | sed -e 's,/[^/]*$,,'`
 	PATH=$tic_head:$PATH
@@ -63,14 +69,16 @@
 	TERMINFO_DIRS=$TERMINFO:$terminfo_dir
 	export TERMINFO_DIRS
 
-	$tic_path -x $terminfo_src >&2
+	"$tic_path" -x "$terminfo_src" >&2
 else
 	tmp_info=
 fi
 
 cat <<EOF
+/* This file was generated by $0 */
+
 /*
- * DO NOT EDIT THIS FILE BY HAND!  It is generated by MKfallback.sh.
+ * DO NOT EDIT THIS FILE BY HAND!
  */
 
 #include <curses.priv.h>
@@ -84,21 +92,21 @@
 
 /* fallback entries for: $* */
 EOF
-	for x in $*
+	for x in "$@"
 	do
 		echo "/* $x */"
-		infocmp -E $x
+		"$infocmp_path" -E "$x" | sed -e 's/\<short\>/NCURSES_INT2/g'
 	done
 
 	cat <<EOF
-static const TERMTYPE fallbacks[$#] =
+static const TERMTYPE2 fallbacks[$#] =
 {
 EOF
 	comma=""
-	for x in $*
+	for x in "$@"
 	do
 		echo "$comma /* $x */"
-		infocmp -e $x
+		"$infocmp_path" -e "$x"
 		comma=","
 	done
 
@@ -109,28 +117,48 @@
 fi
 
 cat <<EOF
-NCURSES_EXPORT(const TERMTYPE *) _nc_fallback (const char *name GCC_UNUSED)
+NCURSES_EXPORT(const TERMTYPE2 *)
+_nc_fallback2 (const char *name GCC_UNUSED)
 {
 EOF
 
 if [ "$*" ]
 then
 	cat <<EOF
-    const TERMTYPE	*tp;
+    const TERMTYPE2	*tp;
 
     for (tp = fallbacks;
-	 	tp < fallbacks + sizeof(fallbacks)/sizeof(TERMTYPE);
-	 	tp++)
-	if (_nc_name_match(tp->term_names, name, "|"))
+	 tp < fallbacks + sizeof(fallbacks)/sizeof(TERMTYPE2);
+	 tp++) {
+	if (_nc_name_match(tp->term_names, name, "|")) {
 	    return(tp);
+	}
+    }
 EOF
 else
 	echo "	/* the fallback list is empty */";
 fi
 
 cat <<EOF
-	return((TERMTYPE *)0);
+    return((const TERMTYPE2 *)0);
 }
+
+#if NCURSES_EXT_NUMBERS
+#undef _nc_fallback
+
+NCURSES_EXPORT(const TERMTYPE *)
+_nc_fallback (const char *name)
+{
+    const TERMTYPE2 *tp = _nc_fallback2(name);
+    const TERMTYPE *result = 0;
+    if (tp != 0) {
+	static TERMTYPE temp;
+	_nc_export_termtype2(&temp, tp);
+	result = &temp;
+    }
+    return result;
+}
+#endif
 EOF
 
 if test -n "$tmp_info" ; then
diff --git a/ncurses/tinfo/MKkeys_list.sh b/ncurses/tinfo/MKkeys_list.sh
index 14017b0..05ac8cf 100755
--- a/ncurses/tinfo/MKkeys_list.sh
+++ b/ncurses/tinfo/MKkeys_list.sh
@@ -1,7 +1,8 @@
 #! /bin/sh
-# $Id: MKkeys_list.sh,v 1.4 2003/10/25 16:19:54 tom Exp $
+# $Id: MKkeys_list.sh,v 1.9 2024/01/19 12:26:30 tom Exp $
 ##############################################################################
-# Copyright (c) 2001,2003 Free Software Foundation, Inc.                     #
+# Copyright 2019-2022,2024 Thomas E. Dickey                                  #
+# Copyright 2001-2003,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 "Software"), #
@@ -35,24 +36,32 @@
 # Extract function-key names from the Caps file
 #
 : ${AWK-awk}
-DATA=${1-../../include/Caps}
+: ${USE_SIGWINCH-0}
+if test $# != 0
+then
+	DATA="$*"
+else
+	DATA=../../include/Caps
+fi
 
 data=data$$
-trap 'rm -f $data' 0 1 2 5 15
-sed -e 's/[	][	]*/	/g' < $DATA >$data
+trap 'rm -f $data; exit 1' 1 2 3 15
+trap 'rm -f $data' 0
+cat $DATA | sed -e 's/[	][	]*/	/g' >$data
 
 cat <<EOF
 # These definitions were generated by $0 $DATA
 KEY_BREAK
 KEY_SRESET
 KEY_RESET
-KEY_RESIZE
 EOF
+test "$USE_SIGWINCH" = 1 && echo KEY_RESIZE
 
 ${AWK-awk} <$data '
 /^#/		{next;}
 /^capalias/	{next;}
 /^infoalias/	{next;}
+/^userdef/	{next;}
 
 $5 != "-" {
 		if (substr($5, 1, 4) == "KEY_" ) {
diff --git a/ncurses/tinfo/MKnames.awk b/ncurses/tinfo/MKnames.awk
index 7685d18..4594c72 100644
--- a/ncurses/tinfo/MKnames.awk
+++ b/ncurses/tinfo/MKnames.awk
@@ -1,5 +1,6 @@
 ##############################################################################
-# Copyright (c) 2007-2008,2009 Free Software Foundation, Inc.                #
+# Copyright 2019,2020 Thomas E. Dickey                                       #
+# Copyright 1998-2008,2009 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 "Software"), #
@@ -25,7 +26,7 @@
 # use or other dealings in this Software without prior written               #
 # authorization.                                                             #
 ##############################################################################
-# $Id: MKnames.awk,v 1.22 2009/03/21 21:03:39 tom Exp $
+# $Id: MKnames.awk,v 1.24 2020/02/02 23:34:34 tom Exp $
 function large_item(value) {
 	result = sprintf("%d,", offset);
 	offset = offset + length(value) + 1;
@@ -79,7 +80,9 @@
 	}
 
 $1 ~ /^#/		{next;}
+$1 ~ /^(cap|info)alias/	{next;}
 
+$1 == "userdef"		{next;}
 $1 == "SKIPWARN"	{next;}
 
 $3 == "bool"	{
diff --git a/ncurses/tinfo/MKuserdefs.sh b/ncurses/tinfo/MKuserdefs.sh
new file mode 100755
index 0000000..109dd64
--- /dev/null
+++ b/ncurses/tinfo/MKuserdefs.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+##############################################################################
+# Copyright 2019,2020 Thomas E. Dickey                                       #
+#                                                                            #
+# Permission is hereby granted, free of charge, to any person obtaining a    #
+# copy of this software and associated documentation files (the "Software"), #
+# to deal in the Software without restriction, including without limitation  #
+# the rights to use, copy, modify, merge, publish, distribute, distribute    #
+# with modifications, sublicense, and/or sell copies of the Software, and to #
+# permit persons to whom the Software is furnished to do so, subject to the  #
+# following conditions:                                                      #
+#                                                                            #
+# The above copyright notice and this permission notice shall be included in #
+# all copies or substantial portions of the Software.                        #
+#                                                                            #
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   #
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    #
+# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      #
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    #
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        #
+# DEALINGS IN THE SOFTWARE.                                                  #
+#                                                                            #
+# Except as contained in this notice, the name(s) of the above copyright     #
+# holders shall not be used in advertising or otherwise to promote the sale, #
+# use or other dealings in this Software without prior written               #
+# authorization.                                                             #
+##############################################################################
+# $Id: MKuserdefs.sh,v 1.10 2020/02/02 23:34:34 tom Exp $
+AWK=${1-awk}; shift 1
+OPT1=${1-0}; shift 1
+
+cat <<EOF
+/*
+ * generated by $0
+ */
+
+EOF
+
+cat <<'EOF'
+/*
+ *    comp_userdefs.c -- The names of widely used user-defined capabilities
+ *                       indexed via a hash table for the compiler.
+ *
+ */
+
+#include <curses.priv.h>
+#include <tic.h>
+#include <hashsize.h>
+
+#if NCURSES_XNAMES
+EOF
+
+cat "$@" | ./make_hash 1 user $OPT1
+
+cat <<EOF
+
+#define USERTABSIZE SIZEOF(user_names_data)
+
+#if $OPT1
+static void
+next_string(const char *strings, unsigned *offset)
+{
+    *offset += (unsigned) strlen(strings + *offset) + 1;
+}
+
+static const struct user_table_entry *
+_nc_build_names(struct user_table_entry **actual,
+		const user_table_data *source,
+		const char *strings)
+{
+    if (*actual == 0) {
+	*actual = typeCalloc(struct user_table_entry, USERTABSIZE);
+	if (*actual != 0) {
+	    unsigned n;
+	    unsigned len = 0;
+	    for (n = 0; n < USERTABSIZE; ++n) {
+		(*actual)[n].ute_name = strings + len;
+		(*actual)[n].ute_type = (int) source[n].ute_type;
+		(*actual)[n].ute_argc = source[n].ute_argc;
+		(*actual)[n].ute_args = source[n].ute_args;
+		(*actual)[n].ute_index = source[n].ute_index;
+		(*actual)[n].ute_link = source[n].ute_link;
+		next_string(strings, &len);
+	    }
+	}
+    }
+    return *actual;
+}
+
+#define build_names(root) _nc_build_names(&_nc_##root##_table, \\
+					  root##_names_data, \\
+					  root##_names_text)
+#else
+#define build_names(root) _nc_ ## root ## _table
+#endif
+
+NCURSES_EXPORT(const struct user_table_entry *) _nc_get_userdefs_table (void)
+{
+    return build_names(user) ;
+}
+
+static HashValue
+info_hash(const char *string)
+{
+    long sum = 0;
+
+    DEBUG(9, ("hashing %s", string));
+    while (*string) {
+	sum += (long) (*string + (*(string + 1) << 8));
+	string++;
+    }
+
+    DEBUG(9, ("sum is %ld", sum));
+    return (HashValue) (sum % HASHTABSIZE);
+}
+
+static int
+compare_info_names(const char *a, const char *b)
+{
+    return !strcmp(a, b);
+}
+
+static const HashData hash_data[] = {
+    { HASHTABSIZE, _nc_user_hash_table, info_hash, compare_info_names }
+};
+
+NCURSES_EXPORT(const HashData *) _nc_get_hash_user (void)
+{
+    return hash_data;
+}
+
+#if NO_LEAKS
+NCURSES_EXPORT(void) _nc_comp_userdefs_leaks(void)
+{
+#if $OPT1
+    FreeIfNeeded(_nc_user_table);
+#endif
+}
+#endif /* NO_LEAKS */
+
+#else /*! NCURSES_XNAMES */
+NCURSES_EXPORT(void) _nc_comp_userdefs(void);
+NCURSES_EXPORT(void) _nc_comp_userdefs(void) { }
+#endif /* NCURSES_XNAMES */
+EOF
diff --git a/ncurses/tinfo/README b/ncurses/tinfo/README
index 14c4220..6157ba1 100644
--- a/ncurses/tinfo/README
+++ b/ncurses/tinfo/README
@@ -1,5 +1,6 @@
 -------------------------------------------------------------------------------
--- Copyright (c) 1998,2006 Free Software Foundation, Inc.                    --
+-- Copyright 2020 Thomas E. Dickey                                           --
+-- Copyright 1998,2006 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             --
@@ -25,7 +26,7 @@
 -- sale, use or other dealings in this Software without prior written        --
 -- authorization.                                                            --
 -------------------------------------------------------------------------------
--- $Id: README,v 1.2 2006/04/22 22:19:37 tom Exp $
+-- $Id: README,v 1.3 2020/02/02 23:34:34 tom Exp $
 -------------------------------------------------------------------------------
 
 The files in this directory (tinfo) are those that support the terminfo
diff --git a/ncurses/tinfo/access.c b/ncurses/tinfo/access.c
index d987687..50a5769 100644
--- a/ncurses/tinfo/access.c
+++ b/ncurses/tinfo/access.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2019-2021,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2011,2012 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            *
@@ -34,20 +35,33 @@
 
 #include <ctype.h>
 
+#ifndef USE_ROOT_ACCESS
+#if HAVE_SETFSUID
+#include <sys/fsuid.h>
+#else
+#include <sys/stat.h>
+#endif
+#endif
+
+#if HAVE_GETAUXVAL && HAVE_SYS_AUXV_H && defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 19)
+#include <sys/auxv.h>
+#define USE_GETAUXVAL 1
+#else
+#define USE_GETAUXVAL 0
+#endif
+
 #include <tic.h>
 
-MODULE_ID("$Id: access.c,v 1.23 2012/09/01 19:21:29 tom Exp $")
-
-#ifdef __TANDEM
-#define ROOT_UID 65535
-#endif
-
-#ifndef ROOT_UID
-#define ROOT_UID 0
-#endif
+MODULE_ID("$Id: access.c,v 1.37 2023/06/24 21:55:09 tom Exp $")
 
 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
 
+#ifdef _NC_MSC
+# define ACCESS(FN, MODE) access((FN), (MODE)&(R_OK|W_OK))
+#else
+# define ACCESS access
+#endif
+
 NCURSES_EXPORT(char *)
 _nc_rootname(char *path)
 {
@@ -56,8 +70,8 @@
     static char *temp;
     char *s;
 
-    temp = strdup(result);
-    result = temp;
+    if ((temp = strdup(result)) != 0)
+	result = temp;
 #if !MIXEDCASE_FILENAMES
     for (s = result; *s != '\0'; ++s) {
 	*s = (char) LOWERCASE(*s);
@@ -119,7 +133,7 @@
 
     if (path == 0) {
 	result = -1;
-    } else if (access(path, mode) < 0) {
+    } else if (ACCESS(path, mode) < 0) {
 	if ((mode & W_OK) != 0
 	    && errno == ENOENT
 	    && strlen(path) < PATH_MAX) {
@@ -134,7 +148,7 @@
 	    if (head == leaf)
 		_nc_STRCPY(head, ".", sizeof(head));
 
-	    result = access(head, R_OK | W_OK | X_OK);
+	    result = ACCESS(head, R_OK | W_OK | X_OK);
 	} else {
 	    result = -1;
 	}
@@ -170,23 +184,101 @@
     return result;
 }
 
-#ifndef USE_ROOT_ENVIRON
+#if HAVE_GETEUID && HAVE_GETEGID
+#define is_posix_elevated() \
+	(getuid() != geteuid() \
+	 || getgid() != getegid())
+#else
+#define is_posix_elevated() FALSE
+#endif
+
+#if HAVE_ISSETUGID
+#define is_elevated() issetugid()
+#elif USE_GETAUXVAL && defined(AT_SECURE)
+#define is_elevated() \
+	(getauxval(AT_SECURE) \
+	 ? TRUE \
+	 : (errno != ENOENT \
+	    ? FALSE \
+	    : is_posix_elevated()))
+#else
+#define is_elevated() is_posix_elevated()
+#endif
+
+#if HAVE_SETFSUID
+#define lower_privileges() \
+	    int save_err = errno; \
+	    setfsuid(getuid()); \
+	    setfsgid(getgid()); \
+	    errno = save_err
+#define resume_elevation() \
+	    save_err = errno; \
+	    setfsuid(geteuid()); \
+	    setfsgid(getegid()); \
+	    errno = save_err
+#else
+#define lower_privileges()	/* nothing */
+#define resume_elevation()	/* nothing */
+#endif
+
 /*
- * Returns true if we allow application to use environment variables that are
- * used for searching lists of directories, etc.
+ * Returns true if not running as root or setuid.  We use this check to allow
+ * applications to use environment variables that are used for searching lists
+ * of directories, etc.
  */
 NCURSES_EXPORT(int)
 _nc_env_access(void)
 {
-#if HAVE_ISSETUGID
-    if (issetugid())
-	return FALSE;
-#elif HAVE_GETEUID && HAVE_GETEGID
-    if (getuid() != geteuid()
-	|| getgid() != getegid())
-	return FALSE;
+    int result = TRUE;
+
+#if HAVE_GETUID && HAVE_GETEUID
+#if !defined(USE_SETUID_ENVIRON)
+    if (is_elevated()) {
+	result = FALSE;
+    }
 #endif
-    /* ...finally, disallow root */
-    return (getuid() != ROOT_UID) && (geteuid() != ROOT_UID);
+#if !defined(USE_ROOT_ENVIRON)
+    if ((getuid() == ROOT_UID) || (geteuid() == ROOT_UID)) {
+	result = FALSE;
+    }
+#endif
+#endif /* HAVE_GETUID && HAVE_GETEUID */
+    return result;
 }
+
+#ifndef USE_ROOT_ACCESS
+/*
+ * Limit privileges if possible; otherwise disallow access for updating files.
+ */
+NCURSES_EXPORT(FILE *)
+_nc_safe_fopen(const char *path, const char *mode)
+{
+    FILE *result = NULL;
+#if HAVE_SETFSUID
+    lower_privileges();
+    result = fopen(path, mode);
+    resume_elevation();
+#else
+    if (!is_elevated() || *mode == 'r') {
+	result = fopen(path, mode);
+    }
 #endif
+    return result;
+}
+
+NCURSES_EXPORT(int)
+_nc_safe_open3(const char *path, int flags, mode_t mode)
+{
+    int result = -1;
+#if HAVE_SETFSUID
+    lower_privileges();
+    result = open(path, flags, mode);
+    resume_elevation();
+#else
+    if (!is_elevated() || (flags & O_RDONLY)) {
+	result = open(path, flags, mode);
+    }
+#endif
+    return result;
+}
+#endif /* USE_ROOT_ACCESS */
diff --git a/ncurses/tinfo/add_tries.c b/ncurses/tinfo/add_tries.c
index 29a1a60..9d21557 100644
--- a/ncurses/tinfo/add_tries.c
+++ b/ncurses/tinfo/add_tries.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.              *
+ * Copyright 2019-2020,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2009,2010 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            *
@@ -38,8 +39,9 @@
 */
 
 #include <curses.priv.h>
+#include <tic.h>
 
-MODULE_ID("$Id: add_tries.c,v 1.10 2010/12/19 01:31:14 tom Exp $")
+MODULE_ID("$Id: add_tries.c,v 1.13 2023/06/24 15:36:13 tom Exp $")
 
 #define SET_TRY(dst,src) if ((dst->ch = *src++) == 128) dst->ch = '\0'
 #define CMP_TRY(a,b) ((a)? (a == b) : (b == 128))
@@ -52,7 +54,7 @@
 
     T((T_CALLED("_nc_add_to_try(%p, %s, %u)"),
        (void *) *tree, _nc_visbuf(str), code));
-    if (txt == 0 || *txt == '\0' || code == 0)
+    if (!VALID_STRING(str) || *txt == '\0' || code == 0)
 	returnCode(ERR);
 
     if ((*tree) != 0) {
@@ -109,6 +111,7 @@
 		savedptr = ptr->child;
 		free(ptr);
 	    }
+	    *tree = NULL;
 	    returnCode(ERR);
 	}
 
diff --git a/ncurses/tinfo/alloc_entry.c b/ncurses/tinfo/alloc_entry.c
index 14ea391..6280ad4 100644
--- a/ncurses/tinfo/alloc_entry.c
+++ b/ncurses/tinfo/alloc_entry.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2013,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,73 +48,89 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: alloc_entry.c,v 1.58 2013/08/17 19:20:38 tom Exp $")
+MODULE_ID("$Id: alloc_entry.c,v 1.79 2023/09/15 08:16:12 tom Exp $")
 
 #define ABSENT_OFFSET    -1
 #define CANCELLED_OFFSET -2
 
-#define MAX_STRTAB	4096	/* documented maximum entry size */
-
 static char *stringbuf;		/* buffer for string capabilities */
 static size_t next_free;	/* next free character in stringbuf */
 
 NCURSES_EXPORT(void)
-_nc_init_entry(TERMTYPE *const tp)
+_nc_init_entry(ENTRY * const tp)
 /* initialize a terminal type data block */
 {
+    DEBUG(2, (T_CALLED("_nc_init_entry(tp=%p)"), (void *) tp));
+
+    if (tp == NULL) {
 #if NO_LEAKS
-    if (tp == 0) {
-	if (stringbuf != 0) {
+	if (stringbuf != NULL) {
 	    FreeAndNull(stringbuf);
 	}
 	return;
-    }
+#else
+	_nc_err_abort("_nc_init_entry called without initialization");
 #endif
+    }
 
-    if (stringbuf == 0)
-	TYPE_MALLOC(char, (size_t) MAX_STRTAB, stringbuf);
+    if (stringbuf == NULL)
+	TYPE_CALLOC(char, (size_t) MAX_ENTRY_SIZE, stringbuf);
 
     next_free = 0;
 
-    _nc_init_termtype(tp);
+    _nc_init_termtype(&(tp->tterm));
+
+    DEBUG(2, (T_RETURN("")));
 }
 
 NCURSES_EXPORT(ENTRY *)
 _nc_copy_entry(ENTRY * oldp)
 {
-    ENTRY *newp = typeCalloc(ENTRY, 1);
+    ENTRY *newp;
 
-    if (newp != 0) {
+    DEBUG(2, (T_CALLED("_nc_copy_entry(oldp=%p)"), (void *) oldp));
+
+    newp = typeCalloc(ENTRY, 1);
+    if (newp != NULL) {
 	*newp = *oldp;
-	_nc_copy_termtype(&(newp->tterm), &(oldp->tterm));
+	_nc_copy_termtype2(&(newp->tterm), &(oldp->tterm));
     }
-    return newp;
+
+    DEBUG(2, (T_RETURN("%p"), (void *) newp));
+    return (newp);
 }
 
 /* save a copy of string in the string buffer */
 NCURSES_EXPORT(char *)
-_nc_save_str(const char *const string)
+_nc_save_str(const char *string)
 {
     char *result = 0;
     size_t old_next_free = next_free;
-    size_t len = strlen(string) + 1;
 
-    if (len == 1 && next_free != 0) {
-	/*
-	 * Cheat a little by making an empty string point to the end of the
-	 * previous string.
-	 */
-	if (next_free < MAX_STRTAB) {
-	    result = (stringbuf + next_free - 1);
+    if (stringbuf != NULL) {
+	size_t len;
+
+	if (!VALID_STRING(string))
+	    string = "";
+	len = strlen(string) + 1;
+
+	if (len == 1 && next_free != 0) {
+	    /*
+	     * Cheat a little by making an empty string point to the end of the
+	     * previous string.
+	     */
+	    if (next_free < MAX_ENTRY_SIZE) {
+		result = (stringbuf + next_free - 1);
+	    }
+	} else if (next_free + len < MAX_ENTRY_SIZE) {
+	    _nc_STRCPY(&stringbuf[next_free], string, MAX_ENTRY_SIZE);
+	    DEBUG(7, ("Saved string %s", _nc_visbuf(string)));
+	    DEBUG(7, ("at location %d", (int) next_free));
+	    next_free += len;
+	    result = (stringbuf + old_next_free);
+	} else {
+	    _nc_warning("Too much data, some is lost: %s", string);
 	}
-    } else if (next_free + len < MAX_STRTAB) {
-	_nc_STRCPY(&stringbuf[next_free], string, MAX_STRTAB);
-	DEBUG(7, ("Saved string %s", _nc_visbuf(string)));
-	DEBUG(7, ("at location %d", (int) next_free));
-	next_free += len;
-	result = (stringbuf + old_next_free);
-    } else {
-	_nc_warning("Too much data, some is lost: %s", string);
     }
     return result;
 }
@@ -125,17 +142,23 @@
     int offsets[MAX_ENTRY_SIZE / sizeof(short)];
     int useoffsets[MAX_USES];
     unsigned i, n;
-    unsigned nuses = ep->nuses;
-    TERMTYPE *tp = &(ep->tterm);
+    unsigned nuses;
+    TERMTYPE2 *tp;
 
+    DEBUG(2, (T_CALLED("_nc_wrap_entry(ep=%p, copy_strings=%d)"), (void *)
+	      ep, copy_strings));
+    if (ep == NULL || stringbuf == NULL)
+	_nc_err_abort("_nc_wrap_entry called without initialization");
+
+    nuses = ep->nuses;
+    tp = &(ep->tterm);
     if (copy_strings) {
 	next_free = 0;		/* clear static storage */
 
 	/* copy term_names, Strings, uses */
 	tp->term_names = _nc_save_str(tp->term_names);
 	for_each_string(i, tp) {
-	    if (tp->Strings[i] != ABSENT_STRING &&
-		tp->Strings[i] != CANCELLED_STRING) {
+	    if (VALID_STRING(tp->Strings[i])) {
 		tp->Strings[i] = _nc_save_str(tp->Strings[i]);
 	    }
 	}
@@ -210,41 +233,148 @@
 #endif
 
     for (i = 0; i < nuses; i++) {
-	if (useoffsets[i] == ABSENT_OFFSET)
+	if (useoffsets[i] == ABSENT_OFFSET) {
 	    ep->uses[i].name = 0;
-	else
-	    ep->uses[i].name = (tp->str_table + useoffsets[i]);
+	} else {
+	    ep->uses[i].name = strdup(tp->str_table + useoffsets[i]);
+	}
     }
+    DEBUG(2, (T_RETURN("")));
 }
 
 NCURSES_EXPORT(void)
-_nc_merge_entry(TERMTYPE *const to, TERMTYPE *const from)
+_nc_merge_entry(ENTRY * const target, ENTRY * const source)
 /* merge capabilities from `from' entry into `to' entry */
 {
+    TERMTYPE2 *to = &(target->tterm);
+    TERMTYPE2 *from = &(source->tterm);
+#if NCURSES_XNAMES
+    TERMTYPE2 copy;
+    size_t str_size, copy_size;
+    char *str_table;
+#endif
     unsigned i;
 
+    if (source == 0 || from == 0 || target == 0 || to == 0)
+	return;
+
 #if NCURSES_XNAMES
+    _nc_copy_termtype2(&copy, from);
+    from = &copy;
     _nc_align_termtype(to, from);
+    /*
+     * compute the maximum size of the string-table.
+     */
+    str_size = strlen(to->term_names) + 1;
+    for_each_string(i, from) {
+	if (VALID_STRING(from->Strings[i]))
+	    str_size += strlen(from->Strings[i]) + 1;
+    }
+    for_each_string(i, to) {
+	if (VALID_STRING(to->Strings[i]))
+	    str_size += strlen(to->Strings[i]) + 1;
+    }
+    /* allocate a string-table large enough for both source/target, and
+     * copy all of the strings into that table.  In the merge, we will
+     * select from the original source/target lists to construct a new
+     * target list.
+     */
+    if (str_size != 0) {
+	char *str_copied;
+	if ((str_table = malloc(str_size)) == NULL)
+	    _nc_err_abort(MSG_NO_MEMORY);
+	str_copied = str_table;
+	_nc_STRCPY(str_copied, to->term_names, str_size);
+	to->term_names = str_copied;
+	copy_size = strlen(str_copied) + 1;
+	str_copied += copy_size;
+	str_size -= copy_size;
+	for_each_string(i, from) {
+	    if (VALID_STRING(from->Strings[i])) {
+		_nc_STRCPY(str_copied, from->Strings[i], str_size);
+		from->Strings[i] = str_copied;
+		copy_size = strlen(str_copied) + 1;
+		str_copied += copy_size;
+		str_size -= copy_size;
+	    }
+	}
+	for_each_string(i, to) {
+	    if (VALID_STRING(to->Strings[i])) {
+		_nc_STRCPY(str_copied, to->Strings[i], str_size);
+		to->Strings[i] = str_copied;
+		copy_size = strlen(str_copied) + 1;
+		str_copied += copy_size;
+		str_size -= copy_size;
+	    }
+	}
+	free(to->str_table);
+	to->str_table = str_table;
+	free(from->str_table);
+    }
+    /*
+     * Do the same for the extended-strings (i.e., lists of capabilities).
+     */
+    str_size = 0;
+    for (i = 0; i < NUM_EXT_NAMES(from); ++i) {
+	if (VALID_STRING(from->ext_Names[i]))
+	    str_size += strlen(from->ext_Names[i]) + 1;
+    }
+    for (i = 0; i < NUM_EXT_NAMES(to); ++i) {
+	if (VALID_STRING(to->ext_Names[i]))
+	    str_size += strlen(to->ext_Names[i]) + 1;
+    }
+    /* allocate a string-table large enough for both source/target, and
+     * copy all of the strings into that table.  In the merge, we will
+     * select from the original source/target lists to construct a new
+     * target list.
+     */
+    if (str_size != 0) {
+	char *str_copied;
+	if ((str_table = malloc(str_size)) == NULL)
+	    _nc_err_abort(MSG_NO_MEMORY);
+	str_copied = str_table;
+	for (i = 0; i < NUM_EXT_NAMES(from); ++i) {
+	    if (VALID_STRING(from->ext_Names[i])) {
+		_nc_STRCPY(str_copied, from->ext_Names[i], str_size);
+		from->ext_Names[i] = str_copied;
+		copy_size = strlen(str_copied) + 1;
+		str_copied += copy_size;
+		str_size -= copy_size;
+	    }
+	}
+	for (i = 0; i < NUM_EXT_NAMES(to); ++i) {
+	    if (VALID_STRING(to->ext_Names[i])) {
+		_nc_STRCPY(str_copied, to->ext_Names[i], str_size);
+		to->ext_Names[i] = str_copied;
+		copy_size = strlen(str_copied) + 1;
+		str_copied += copy_size;
+		str_size -= copy_size;
+	    }
+	}
+	free(to->ext_str_table);
+	to->ext_str_table = str_table;
+	free(from->ext_str_table);
+    }
 #endif
     for_each_boolean(i, from) {
-	if (to->Booleans[i] != (char) CANCELLED_BOOLEAN) {
+	if (to->Booleans[i] != (NCURSES_SBOOL) CANCELLED_BOOLEAN) {
 	    int mergebool = from->Booleans[i];
 
 	    if (mergebool == CANCELLED_BOOLEAN)
 		to->Booleans[i] = FALSE;
 	    else if (mergebool == TRUE)
-		to->Booleans[i] = (char) mergebool;
+		to->Booleans[i] = (NCURSES_SBOOL) mergebool;
 	}
     }
 
     for_each_number(i, from) {
 	if (to->Numbers[i] != CANCELLED_NUMERIC) {
-	    short mergenum = from->Numbers[i];
+	    int mergenum = from->Numbers[i];
 
 	    if (mergenum == CANCELLED_NUMERIC)
 		to->Numbers[i] = ABSENT_NUMERIC;
 	    else if (mergenum != ABSENT_NUMERIC)
-		to->Numbers[i] = mergenum;
+		to->Numbers[i] = (NCURSES_INT2) mergenum;
 	}
     }
 
@@ -263,13 +393,20 @@
 		to->Strings[i] = mergestring;
 	}
     }
+#if NCURSES_XNAMES
+    /* cleanup */
+    free(copy.Booleans);
+    free(copy.Numbers);
+    free(copy.Strings);
+    free(copy.ext_Names);
+#endif
 }
 
 #if NO_LEAKS
 NCURSES_EXPORT(void)
 _nc_alloc_entry_leaks(void)
 {
-    if (stringbuf != 0) {
+    if (stringbuf != NULL) {
 	FreeAndNull(stringbuf);
     }
     next_free = 0;
diff --git a/ncurses/tinfo/alloc_ttype.c b/ncurses/tinfo/alloc_ttype.c
index 35c92dd..304c1b6 100644
--- a/ncurses/tinfo/alloc_ttype.c
+++ b/ncurses/tinfo/alloc_ttype.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1999-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 Thomas E. Dickey                                *
+ * Copyright 1999-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            *
@@ -42,7 +43,7 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: alloc_ttype.c,v 1.27 2013/06/08 16:54:50 tom Exp $")
+MODULE_ID("$Id: alloc_ttype.c,v 1.51 2023/09/09 23:15:53 tom Exp $")
 
 #if NCURSES_XNAMES
 /*
@@ -61,7 +62,7 @@
 	} else if (cmp > 0) {
 	    dst[n++] = *b++;
 	    nb--;
-	} else if (cmp == 0) {
+	} else {
 	    dst[n++] = *a;
 	    a++, b++;
 	    na--, nb--;
@@ -78,37 +79,59 @@
 }
 
 static bool
-find_name(char **table, int length, char *name)
+find_name(char **table, int item, int length, const char *name)
 {
-    while (length-- > 0) {
-	if (!strcmp(*table++, name)) {
-	    DEBUG(4, ("found name '%s'", name));
-	    return TRUE;
+    int n;
+    int result = -1;
+
+    for (n = item; n < length; ++n) {
+	if (!strcmp(table[n], name)) {
+	    DEBUG(4, ("found name '%s' @%d", name, n));
+	    result = n;
+	    break;
 	}
     }
-    DEBUG(4, ("did not find name '%s'", name));
-    return FALSE;
+    if (result < 0) {
+	DEBUG(4, ("did not find name '%s'", name));
+    }
+    return (result >= 0);
 }
 
 #define EXTEND_NUM(num, ext) \
+	DEBUG(4, ("extending " #num " from %d to %d", \
+	 to->num, (unsigned short) (to->num + (ext - to->ext)))); \
 	to->num = (unsigned short) (to->num + (ext - to->ext))
 
 static void
-realign_data(TERMTYPE *to, char **ext_Names,
+realign_data(TERMTYPE2 *to, char **ext_Names,
 	     int ext_Booleans,
 	     int ext_Numbers,
 	     int ext_Strings)
 {
     int n, m, base;
-    int limit = (to->ext_Booleans + to->ext_Numbers + to->ext_Strings);
+    int to_Booleans = to->ext_Booleans;
+    int to_Numbers = to->ext_Numbers;
+    int to_Strings = to->ext_Strings;
+    int to1, to2, from;
+
+    DEBUG(4, ("realign_data %d/%d/%d vs %d/%d/%d",
+	      ext_Booleans,
+	      ext_Numbers,
+	      ext_Strings,
+	      to->ext_Booleans,
+	      to->ext_Numbers,
+	      to->ext_Strings));
 
     if (to->ext_Booleans != ext_Booleans) {
+	to1 = 0;
+	to2 = to_Booleans + to1;
+	from = 0;
 	EXTEND_NUM(num_Booleans, ext_Booleans);
 	TYPE_REALLOC(NCURSES_SBOOL, to->num_Booleans, to->Booleans);
 	for (n = to->ext_Booleans - 1,
 	     m = ext_Booleans - 1,
 	     base = to->num_Booleans - (m + 1); m >= 0; m--) {
-	    if (find_name(to->ext_Names, limit, ext_Names[m])) {
+	    if (find_name(to->ext_Names, to1, to2, ext_Names[m + from])) {
 		to->Booleans[base + m] = to->Booleans[base + n--];
 	    } else {
 		to->Booleans[base + m] = FALSE;
@@ -118,12 +141,15 @@
     }
 
     if (to->ext_Numbers != ext_Numbers) {
+	to1 = to_Booleans;
+	to2 = to_Numbers + to1;
+	from = ext_Booleans;
 	EXTEND_NUM(num_Numbers, ext_Numbers);
-	TYPE_REALLOC(short, to->num_Numbers, to->Numbers);
+	TYPE_REALLOC(NCURSES_INT2, to->num_Numbers, to->Numbers);
 	for (n = to->ext_Numbers - 1,
 	     m = ext_Numbers - 1,
 	     base = to->num_Numbers - (m + 1); m >= 0; m--) {
-	    if (find_name(to->ext_Names, limit, ext_Names[m + ext_Booleans])) {
+	    if (find_name(to->ext_Names, to1, to2, ext_Names[m + from])) {
 		to->Numbers[base + m] = to->Numbers[base + n--];
 	    } else {
 		to->Numbers[base + m] = ABSENT_NUMERIC;
@@ -131,13 +157,17 @@
 	}
 	to->ext_Numbers = UShort(ext_Numbers);
     }
+
     if (to->ext_Strings != ext_Strings) {
+	to1 = to_Booleans + to_Numbers;
+	to2 = to_Strings + to1;
+	from = ext_Booleans + ext_Numbers;
 	EXTEND_NUM(num_Strings, ext_Strings);
 	TYPE_REALLOC(char *, to->num_Strings, to->Strings);
 	for (n = to->ext_Strings - 1,
 	     m = ext_Strings - 1,
 	     base = to->num_Strings - (m + 1); m >= 0; m--) {
-	    if (find_name(to->ext_Names, limit, ext_Names[m + ext_Booleans + ext_Numbers])) {
+	    if (find_name(to->ext_Names, to1, to2, ext_Names[m + from])) {
 		to->Strings[base + m] = to->Strings[base + n--];
 	    } else {
 		to->Strings[base + m] = ABSENT_STRING;
@@ -151,7 +181,7 @@
  * Returns the first index in ext_Names[] for the given token-type
  */
 static unsigned
-_nc_first_ext_name(TERMTYPE *tp, int token_type)
+_nc_first_ext_name(TERMTYPE2 *tp, int token_type)
 {
     unsigned first;
 
@@ -176,7 +206,7 @@
  * Returns the last index in ext_Names[] for the given token-type
  */
 static unsigned
-_nc_last_ext_name(TERMTYPE *tp, int token_type)
+_nc_last_ext_name(TERMTYPE2 *tp, int token_type)
 {
     unsigned last;
 
@@ -199,7 +229,7 @@
  * Lookup an entry from extended-names, returning -1 if not found
  */
 static int
-_nc_find_ext_name(TERMTYPE *tp, char *name, int token_type)
+_nc_find_ext_name(TERMTYPE2 *tp, char *name, int token_type)
 {
     unsigned j;
     unsigned first = _nc_first_ext_name(tp, token_type);
@@ -218,7 +248,7 @@
  * (e.g., Booleans[]).
  */
 static int
-_nc_ext_data_index(TERMTYPE *tp, int n, int token_type)
+_nc_ext_data_index(TERMTYPE2 *tp, int n, int token_type)
 {
     switch (token_type) {
     case BOOLEAN:
@@ -241,13 +271,14 @@
  * data.
  */
 static bool
-_nc_del_ext_name(TERMTYPE *tp, char *name, int token_type)
+_nc_del_ext_name(TERMTYPE2 *tp, char *name, int token_type)
 {
-    int j;
-    int first, last;
+    int first;
 
     if ((first = _nc_find_ext_name(tp, name, token_type)) >= 0) {
-	last = (int) NUM_EXT_NAMES(tp) - 1;
+	int j;
+	int last = (int) NUM_EXT_NAMES(tp) - 1;
+
 	for (j = first; j < last; j++) {
 	    tp->ext_Names[j] = tp->ext_Names[j + 1];
 	}
@@ -285,7 +316,7 @@
  * index into the corresponding data array is returned.
  */
 static int
-_nc_ins_ext_name(TERMTYPE *tp, char *name, int token_type)
+_nc_ins_ext_name(TERMTYPE2 *tp, char *name, int token_type)
 {
     unsigned first = _nc_first_ext_name(tp, token_type);
     unsigned last = _nc_last_ext_name(tp, token_type);
@@ -319,7 +350,7 @@
     case NUMBER:
 	tp->ext_Numbers++;
 	tp->num_Numbers++;
-	TYPE_REALLOC(short, tp->num_Numbers, tp->Numbers);
+	TYPE_REALLOC(NCURSES_INT2, tp->num_Numbers, tp->Numbers);
 	for (k = (unsigned) (tp->num_Numbers - 1); k > j; k--)
 	    tp->Numbers[k] = tp->Numbers[k - 1];
 	break;
@@ -340,12 +371,15 @@
  * cancellation of a name that is inherited from another entry.
  */
 static void
-adjust_cancels(TERMTYPE *to, TERMTYPE *from)
+adjust_cancels(TERMTYPE2 *to, TERMTYPE2 *from)
 {
     int first = to->ext_Booleans + to->ext_Numbers;
     int last = first + to->ext_Strings;
     int j, k;
 
+    DEBUG(3, (T_CALLED("adjust_cancels(%s), from(%s)"),
+	      NonNull(to->term_names),
+	      NonNull(from->term_names)));
     for (j = first; j < last;) {
 	char *name = to->ext_Names[j];
 	int j_str = to->num_Strings - first - to->ext_Strings;
@@ -382,38 +416,47 @@
 	    j++;
 	}
     }
+    DEBUG(3, (T_RETURN("")));
 }
 
 NCURSES_EXPORT(void)
-_nc_align_termtype(TERMTYPE *to, TERMTYPE *from)
+_nc_align_termtype(TERMTYPE2 *to, TERMTYPE2 *from)
 {
-    int na = (int) NUM_EXT_NAMES(to);
-    int nb = (int) NUM_EXT_NAMES(from);
-    int n;
-    bool same;
+    int na;
+    int nb;
     char **ext_Names;
-    int ext_Booleans, ext_Numbers, ext_Strings;
-    bool used_ext_Names = FALSE;
 
-    DEBUG(2, ("align_termtype to(%d:%s), from(%d:%s)", na, to->term_names,
-	      nb, from->term_names));
+    na = to ? ((int) NUM_EXT_NAMES(to)) : 0;
+    nb = from ? ((int) NUM_EXT_NAMES(from)) : 0;
 
-    if (na != 0 || nb != 0) {
+    DEBUG(2, (T_CALLED("_nc_align_termtype to(%d:%s), from(%d:%s)"),
+	      na, to ? NonNull(to->term_names) : "?",
+	      nb, from ? NonNull(from->term_names) : "?"));
+
+    if (to != NULL && from != NULL && (na != 0 || nb != 0)) {
+	int ext_Booleans, ext_Numbers, ext_Strings;
+	bool used_ext_Names = FALSE;
+
 	if ((na == nb)		/* check if the arrays are equivalent */
 	    &&(to->ext_Booleans == from->ext_Booleans)
 	    && (to->ext_Numbers == from->ext_Numbers)
 	    && (to->ext_Strings == from->ext_Strings)) {
+	    int n;
+	    bool same;
+
 	    for (n = 0, same = TRUE; n < na; n++) {
 		if (strcmp(to->ext_Names[n], from->ext_Names[n])) {
 		    same = FALSE;
 		    break;
 		}
 	    }
-	    if (same)
+	    if (same) {
+		DEBUG(2, (T_RETURN("")));
 		return;
+	    }
 	}
 	/*
-	 * This is where we pay for having a simple extension representation. 
+	 * This is where we pay for having a simple extension representation.
 	 * Allocate a new ext_Names array and merge the two ext_Names arrays
 	 * into it, updating to's counts for booleans, etc.  Fortunately we do
 	 * this only for the terminfo compiler (tic) and comparer (infocmp).
@@ -470,42 +513,201 @@
 	if (!used_ext_Names)
 	    free(ext_Names);
     }
+    DEBUG(2, (T_RETURN("")));
 }
 #endif
 
-NCURSES_EXPORT(void)
-_nc_copy_termtype(TERMTYPE *dst, const TERMTYPE *src)
+#define srcINT 1
+#define dstINT 2
+
+/*
+ * TERMTYPE and TERMTYPE2 differ only with regard to the values in Numbers.
+ * Use 'mode' to decide which to use.
+ */
+static void
+copy_termtype(TERMTYPE2 *dst, const TERMTYPE2 *src, int mode)
 {
-#if NCURSES_XNAMES
     unsigned i;
+    int pass;
+    char *new_table;
+    size_t new_table_size;
+#if NCURSES_EXT_NUMBERS
+    short *oldptr = 0;
+    int *newptr = 0;
 #endif
 
+    DEBUG(2, (T_CALLED("copy_termtype(dst=%p, src=%p, mode=%d)"), (void *)
+	      dst, (const void *) src, mode));
     *dst = *src;		/* ...to copy the sizes and string-tables */
 
     TYPE_MALLOC(NCURSES_SBOOL, NUM_BOOLEANS(dst), dst->Booleans);
-    TYPE_MALLOC(short, NUM_NUMBERS(dst), dst->Numbers);
     TYPE_MALLOC(char *, NUM_STRINGS(dst), dst->Strings);
 
     memcpy(dst->Booleans,
 	   src->Booleans,
 	   NUM_BOOLEANS(dst) * sizeof(dst->Booleans[0]));
-    memcpy(dst->Numbers,
-	   src->Numbers,
-	   NUM_NUMBERS(dst) * sizeof(dst->Numbers[0]));
     memcpy(dst->Strings,
 	   src->Strings,
 	   NUM_STRINGS(dst) * sizeof(dst->Strings[0]));
 
-    /* FIXME: we probably should also copy str_table and ext_str_table,
-     * but tic and infocmp are not written to exploit that (yet).
-     */
+    new_table = NULL;
+    new_table_size = 0;
+    for (pass = 0; pass < 2; ++pass) {
+	size_t str_size = 0;
+	if (src->term_names != NULL) {
+	    if (pass) {
+		dst->term_names = new_table + str_size;
+		_nc_STRCPY(dst->term_names + str_size,
+			   src->term_names,
+			   new_table_size - str_size);
+	    }
+	    str_size += strlen(src->term_names) + 1;
+	}
+	for_each_string(i, src) {
+	    if (VALID_STRING(src->Strings[i])) {
+		if (pass) {
+		    _nc_STRCPY(new_table + str_size,
+			       src->Strings[i],
+			       new_table_size - str_size);
+		    dst->Strings[i] = new_table + str_size;
+		}
+		str_size += strlen(src->Strings[i]) + 1;
+	    }
+	}
+	if (pass) {
+	    dst->str_table = new_table;
+	} else {
+	    ++str_size;
+	    if ((new_table = malloc(str_size)) == NULL)
+		_nc_err_abort(MSG_NO_MEMORY);
+	    new_table_size = str_size;
+	}
+    }
+
+#if NCURSES_EXT_NUMBERS
+    if ((mode & dstINT) == 0) {
+	DEBUG(2, ("...convert int ->short"));
+	TYPE_MALLOC(short, NUM_NUMBERS(dst), oldptr);
+	((TERMTYPE *) dst)->Numbers = oldptr;
+    } else {
+	DEBUG(2, ("...copy without changing size"));
+	TYPE_MALLOC(int, NUM_NUMBERS(dst), newptr);
+	dst->Numbers = newptr;
+    }
+    if ((mode == srcINT) && (oldptr != 0)) {
+	DEBUG(2, ("...copy int ->short"));
+	for (i = 0; i < NUM_NUMBERS(dst); ++i) {
+	    if (src->Numbers[i] > MAX_OF_TYPE(short)) {
+		oldptr[i] = MAX_OF_TYPE(short);
+	    } else {
+		oldptr[i] = (short) src->Numbers[i];
+	    }
+	}
+    } else if ((mode == dstINT) && (newptr != 0)) {
+	DEBUG(2, ("...copy short ->int"));
+	for (i = 0; i < NUM_NUMBERS(dst); ++i) {
+	    newptr[i] = ((const short *) (src->Numbers))[i];
+	}
+    } else {
+	DEBUG(2, ("...copy %s without change",
+		  (mode & dstINT)
+		  ? "int"
+		  : "short"));
+	memcpy(dst->Numbers,
+	       src->Numbers,
+	       NUM_NUMBERS(dst) * ((mode & dstINT)
+				   ? sizeof(int)
+				   : sizeof(short)));
+    }
+#else
+    (void) mode;
+    TYPE_MALLOC(short, NUM_NUMBERS(dst), dst->Numbers);
+    memcpy(dst->Numbers,
+	   src->Numbers,
+	   NUM_NUMBERS(dst) * sizeof(dst->Numbers[0]));
+#endif
 
 #if NCURSES_XNAMES
     if ((i = NUM_EXT_NAMES(src)) != 0) {
 	TYPE_MALLOC(char *, i, dst->ext_Names);
 	memcpy(dst->ext_Names, src->ext_Names, i * sizeof(char *));
+
+	new_table = NULL;
+	new_table_size = 0;
+	for (pass = 0; pass < 2; ++pass) {
+	    size_t str_size = 0;
+	    char *raw_data = src->ext_str_table;
+	    if (raw_data != NULL) {
+		for (i = 0; i < src->ext_Strings; ++i) {
+		    size_t skip = strlen(raw_data) + 1;
+		    if (skip != 1) {
+			if (pass) {
+			    _nc_STRCPY(new_table + str_size,
+				       raw_data,
+				       new_table_size - str_size);
+			}
+			str_size += skip;
+			raw_data += skip;
+		    }
+		}
+	    }
+	    for (i = 0; i < NUM_EXT_NAMES(dst); ++i) {
+		if (VALID_STRING(src->ext_Names[i])) {
+		    if (pass) {
+			_nc_STRCPY(new_table + str_size,
+				   src->ext_Names[i],
+				   new_table_size - str_size);
+			dst->ext_Names[i] = new_table + str_size;
+		    }
+		    str_size += strlen(src->ext_Names[i]) + 1;
+		}
+	    }
+	    if (pass) {
+		dst->ext_str_table = new_table;
+	    } else {
+		++str_size;
+		if ((new_table = calloc(str_size, 1)) == NULL)
+		    _nc_err_abort(MSG_NO_MEMORY);
+		new_table_size = str_size;
+	    }
+	}
     } else {
 	dst->ext_Names = 0;
     }
 #endif
+    (void) new_table_size;
+    DEBUG(2, (T_RETURN("")));
 }
+
+NCURSES_EXPORT(void)
+_nc_copy_termtype(TERMTYPE *dst, const TERMTYPE *src)
+{
+    DEBUG(2, (T_CALLED("_nc_copy_termtype(dst=%p, src=%p)"), (void *) dst,
+	      (const void *) src));
+    copy_termtype((TERMTYPE2 *) dst, (const TERMTYPE2 *) src, 0);
+    DEBUG(2, (T_RETURN("")));
+}
+
+#if NCURSES_EXT_NUMBERS
+NCURSES_EXPORT(void)
+_nc_copy_termtype2(TERMTYPE2 *dst, const TERMTYPE2 *src)
+{
+    DEBUG(2, (T_CALLED("_nc_copy_termtype2(dst=%p, src=%p)"), (void *) dst,
+	      (const void *) src));
+    copy_termtype(dst, src, srcINT | dstINT);
+    DEBUG(2, (T_RETURN("")));
+}
+
+/*
+ * Use this for exporting the internal TERMTYPE2 to the legacy format used via
+ * the CUR macro by applications.
+ */
+NCURSES_EXPORT(void)
+_nc_export_termtype2(TERMTYPE *dst, const TERMTYPE2 *src)
+{
+    DEBUG(2, (T_CALLED("_nc_export_termtype2(dst=%p, src=%p)"), (void *)
+	      dst, (const void *) src));
+    copy_termtype((TERMTYPE2 *) dst, src, srcINT);
+    DEBUG(2, (T_RETURN("")));
+}
+#endif /* NCURSES_EXT_NUMBERS */
diff --git a/ncurses/tinfo/captoinfo.c b/ncurses/tinfo/captoinfo.c
index e02e622..7e14731 100644
--- a/ncurses/tinfo/captoinfo.c
+++ b/ncurses/tinfo/captoinfo.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 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            *
@@ -33,12 +34,16 @@
  ****************************************************************************/
 
 /*
- *	captoinfo.c --- conversion between termcap and terminfo formats
+ *	captoinfo.c
+ *
+ *	Provide conversion in both directions between termcap and terminfo.
+ *
+ * cap-to-info --- conversion between termcap and terminfo formats
  *
  *	The captoinfo() code was swiped from Ross Ridge's mytinfo package,
  *	adapted to fit ncurses by Eric S. Raymond <esr@snark.thyrsus.com>.
  *
- *	There is just one entry point:
+ *	It has just one entry point:
  *
  *	char *_nc_captoinfo(n, s, parameterized)
  *
@@ -93,7 +98,13 @@
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: captoinfo.c,v 1.77 2012/12/30 00:50:40 tom Exp $")
+MODULE_ID("$Id: captoinfo.c,v 1.102 2021/09/04 10:29:15 tom Exp $")
+
+#if 0
+#define DEBUG_THIS(p) DEBUG(9, p)
+#else
+#define DEBUG_THIS(p)		/* nothing */
+#endif
 
 #define MAX_PUSHED	16	/* max # args we can push onto the stack */
 
@@ -181,7 +192,7 @@
 	case '$':
 	case '\\':
 	case '%':
-	    c = (unsigned char) (*sp);
+	    c = UChar(*sp);
 	    len = 2;
 	    break;
 	case '\0':
@@ -194,29 +205,36 @@
 	case '3':
 	    len = 1;
 	    while (isdigit(UChar(*sp))) {
-		c = (unsigned char) (8 * c + (*sp++ - '0'));
+		c = UChar(8 * c + (*sp++ - '0'));
 		len++;
 	    }
 	    break;
 	default:
-	    c = (unsigned char) (*sp);
-	    len = 2;
+	    c = UChar(*sp);
+	    len = (c != '\0') ? 2 : 1;
 	    break;
 	}
 	break;
     case '^':
-	c = (unsigned char) (*++sp & 0x1f);
 	len = 2;
+	c = UChar(*++sp);
+	if (c == '?') {
+	    c = 127;
+	} else if (c == '\0') {
+	    len = 1;
+	} else {
+	    c &= 0x1f;
+	}
 	break;
     default:
-	c = (unsigned char) (*sp);
-	len = 1;
+	c = UChar(*sp);
+	len = (c != '\0') ? 1 : 0;
     }
     if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
 	dp = save_string(dp, "%\'");
 	dp = save_char(dp, c);
 	dp = save_char(dp, '\'');
-    } else {
+    } else if (c != '\0') {
 	dp = save_string(dp, "%{");
 	if (c > 99)
 	    dp = save_char(dp, c / 100 + '0');
@@ -232,6 +250,8 @@
 getparm(int parm, int n)
 /* push n copies of param on the terminfo stack if not already there */
 {
+    int nn;
+
     if (seenr) {
 	if (parm == 1)
 	    parm = 2;
@@ -239,7 +259,7 @@
 	    parm = 1;
     }
 
-    while (n--) {
+    for (nn = 0; nn < n; ++nn) {
 	dp = save_string(dp, "%p");
 	dp = save_char(dp, '0' + parm);
     }
@@ -248,7 +268,7 @@
 	if (n > 1) {
 	    _nc_warning("string may not be optimal");
 	    dp = save_string(dp, "%Pa");
-	    while (n--) {
+	    while (n-- > 0) {
 		dp = save_string(dp, "%ga");
 	    }
 	}
@@ -288,6 +308,8 @@
     seenr = 0;
     param = 1;
 
+    DEBUG_THIS(("_nc_captoinfo params %d, %s", parameterized, s));
+
     dp = init_string();
 
     /* skip the initial padding (if we haven't been told not to) */
@@ -295,7 +317,7 @@
     if (s == 0)
 	s = "";
     if (parameterized >= 0 && isdigit(UChar(*s)))
-	for (capstart = s;; s++)
+	for (capstart = s; *s != '\0'; s++)
 	    if (!(isdigit(UChar(*s)) || *s == '*' || *s == '.'))
 		break;
 
@@ -309,7 +331,7 @@
 	    }
 	    switch (*s++) {
 	    case '%':
-		dp = save_char(dp, '%');
+		dp = save_string(dp, "%%");
 		break;
 	    case 'r':
 		if (seenr++ == 1) {
@@ -342,13 +364,18 @@
 		dp = save_string(dp, "%{2}%*%-");
 		break;
 	    case '>':
-		getparm(param, 2);
 		/* %?%{x}%>%t%{y}%+%; */
-		dp = save_string(dp, "%?");
-		s += cvtchar(s);
-		dp = save_string(dp, "%>%t");
-		s += cvtchar(s);
-		dp = save_string(dp, "%+%;");
+		if (s[0] && s[1]) {
+		    getparm(param, 2);
+		    dp = save_string(dp, "%?");
+		    s += cvtchar(s);
+		    dp = save_string(dp, "%>%t");
+		    s += cvtchar(s);
+		    dp = save_string(dp, "%+%;");
+		} else {
+		    _nc_warning("expected two characters after %%>");
+		    dp = save_string(dp, "%>");
+		}
 		break;
 	    case 'a':
 		if ((*s == '=' || *s == '+' || *s == '-'
@@ -429,12 +456,17 @@
 		pop();
 		break;
 	    case '0':		/* not clear any of the historical termcaps did this */
-		if (*s == '3')
+		if (*s == '3') {
+		    ++s;
 		    goto see03;
-		else if (*s != '2')
-		    goto invalid;
-		/* FALLTHRU */
+		}
+		if (*s == '2') {
+		    ++s;
+		    goto see02;
+		}
+		goto invalid;
 	    case '2':
+	      see02:
 		getparm(param, 1);
 		dp = save_string(dp, "%2d");
 		pop();
@@ -469,7 +501,8 @@
 	    }
 	    break;
 	default:
-	    dp = save_char(dp, *s++);
+	    if (*s != '\0')
+		dp = save_char(dp, *s++);
 	    break;
 	}
     }
@@ -480,7 +513,7 @@
      */
     if (capstart) {
 	dp = save_string(dp, "$<");
-	for (s = capstart;; s++)
+	for (s = capstart; *s != '\0'; s++)
 	    if (isdigit(UChar(*s)) || *s == '*' || *s == '.')
 		dp = save_char(dp, *s);
 	    else
@@ -489,6 +522,9 @@
     }
 
     (void) save_char(dp, '\0');
+
+    DEBUG_THIS(("... _nc_captoinfo %s", NonNull(my_string)));
+
     return (my_string);
 }
 
@@ -525,13 +561,13 @@
 static char *
 save_tc_char(char *bufptr, int c1)
 {
-    char temp[80];
-
     if (is7bits(c1) && isprint(c1)) {
 	if (c1 == ':' || c1 == '\\')
 	    bufptr = save_char(bufptr, '\\');
 	bufptr = save_char(bufptr, c1);
     } else {
+	char temp[80];
+
 	if (c1 == (c1 & 0x1f)) {	/* iscntrl() returns T on 255 */
 	    _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
 			"%.20s", unctrl((chtype) c1));
@@ -554,6 +590,8 @@
 }
 
 /*
+ * info-to-cap --- conversion between terminfo and termcap formats
+ *
  * Here are the capabilities infotocap assumes it can translate to:
  *
  *     %%       output `%'
@@ -571,6 +609,8 @@
  *     %m       exclusive-or all parameters with 0177 (not in 4.4BSD)
  */
 
+#define octal_fixup(n, c) fixups[n].ch = ((fixups[n].ch << 3) | ((c) - '0'))
+
 /*
  * Convert a terminfo string to termcap format.  Parameters are as in
  * _nc_captoinfo().
@@ -586,12 +626,23 @@
     char *bufptr = init_string();
     char octal[4];
     int len;
+    int digits;
     bool syntax_error = FALSE;
+    int myfix = 0;
+    struct {
+	int ch;
+	int offset;
+    } fixups[MAX_TC_FIXUPS];
+
+    DEBUG_THIS(("_nc_infotocap %s params %d, %s",
+		_nc_strict_bsd ? "strict" : "loose",
+		parameterized,
+		_nc_visbuf(str)));
 
     /* we may have to move some trailing mandatory padding up front */
     padding = str + strlen(str) - 1;
     if (padding > str && *padding == '>') {
-	if (*--padding == '/')
+	if (padding > (str + 1) && *--padding == '/')
 	    --padding;
 	while (isdigit(UChar(*padding)) || *padding == '.' || *padding == '*')
 	    padding--;
@@ -603,7 +654,9 @@
 	    bufptr = save_char(bufptr, *padding++);
     }
 
-    for (; *str && ((trimmed == 0) || (str < trimmed)); str++) {
+    for (; !syntax_error &&
+	 *str &&
+	 ((trimmed == 0) || (str < trimmed)); str++) {
 	int c1, c2;
 	char *cp = 0;
 
@@ -611,10 +664,23 @@
 	    if (str[1] == '\0' || (str + 1) == trimmed) {
 		bufptr = save_string(bufptr, "\\136");
 		++str;
+	    } else if (str[1] == '?') {
+		/*
+		 * Although the 4.3BSD termcap file has an instance of "kb=^?",
+		 * that appears to be just cut/paste since neither 4.3BSD nor
+		 * 4.4BSD termcap interprets "^?" as DEL.
+		 */
+		bufptr = save_string(bufptr, "\\177");
+		++str;
 	    } else {
 		bufptr = save_char(bufptr, *str++);
 		bufptr = save_char(bufptr, *str);
 	    }
+	} else if (str[0] == ':') {
+	    bufptr = save_char(bufptr, '\\');
+	    bufptr = save_char(bufptr, '0');
+	    bufptr = save_char(bufptr, '7');
+	    bufptr = save_char(bufptr, '2');
 	} else if (str[0] == '\\') {
 	    if (str[1] == '\0' || (str + 1) == trimmed) {
 		bufptr = save_string(bufptr, "\\134");
@@ -625,17 +691,20 @@
 	    } else if (str[1] == ',') {
 		bufptr = save_char(bufptr, *++str);
 	    } else {
-		int xx1, xx2;
+		int xx1;
 
 		bufptr = save_char(bufptr, *str++);
 		xx1 = *str;
 		if (_nc_strict_bsd) {
-		    if (isdigit(UChar(xx1))) {
-			int pad = 0;
 
-			if (!isdigit(UChar(str[1])))
+		    if (isoctal(UChar(xx1))) {
+			int pad = 0;
+			int xx2;
+			int fix = 0;
+
+			if (!isoctal(UChar(str[1])))
 			    pad = 2;
-			else if (str[1] && !isdigit(UChar(str[2])))
+			else if (str[1] && !isoctal(UChar(str[2])))
 			    pad = 1;
 
 			/*
@@ -650,10 +719,31 @@
 			    xx2 = '0';
 			    pad = 0;	/* FIXME - optionally pad to 3 digits */
 			}
+			if (myfix < MAX_TC_FIXUPS) {
+			    fix = 3 - pad;
+			    fixups[myfix].ch = 0;
+			    fixups[myfix].offset = (int) (bufptr
+							  - my_string
+							  - 1);
+			}
 			while (pad-- > 0) {
 			    bufptr = save_char(bufptr, xx2);
+			    if (myfix < MAX_TC_FIXUPS) {
+				fixups[myfix].ch <<= 3;
+				fixups[myfix].ch |= (xx2 - '0');
+			    }
 			    xx2 = '0';
 			}
+			if (myfix < MAX_TC_FIXUPS) {
+			    int n;
+			    for (n = 0; n < fix; ++n) {
+				fixups[myfix].ch <<= 3;
+				fixups[myfix].ch |= (str[n] - '0');
+			    }
+			    if (fixups[myfix].ch < 32) {
+				++myfix;
+			    }
+			}
 		    } else if (strchr("E\\nrtbf", xx1) == 0) {
 			switch (xx1) {
 			case 'e':
@@ -689,6 +779,24 @@
 			    break;
 			}
 		    }
+		} else {
+		    if (myfix < MAX_TC_FIXUPS && isoctal(UChar(xx1))) {
+			bool will_fix = TRUE;
+			int n;
+
+			fixups[myfix].ch = 0;
+			fixups[myfix].offset = (int) (bufptr - my_string - 1);
+			for (n = 0; n < 3; ++n) {
+			    if (isoctal(str[n])) {
+				octal_fixup(myfix, str[n]);
+			    } else {
+				will_fix = FALSE;
+				break;
+			    }
+			}
+			if (will_fix && (fixups[myfix].ch < 32))
+			    ++myfix;
+		    }
 		}
 		bufptr = save_char(bufptr, xx1);
 	    }
@@ -735,8 +843,9 @@
 	} else if ((len = bcd_expression(str)) != 0) {
 	    str += len;
 	    bufptr = save_string(bufptr, "%B");
-	} else if ((sscanf(str, "%%{%d}%%+%%c", &c1) == 1
-		    || sscanf(str, "%%'%c'%%+%%c", &ch1) == 1)
+	} else if ((sscanf(str, "%%{%d}%%+%%%c", &c1, &ch2) == 2
+		    || sscanf(str, "%%'%c'%%+%%%c", &ch1, &ch2) == 2)
+		   && ch2 == 'c'
 		   && (cp = strchr(str, '+'))) {
 	    str = cp + 2;
 	    bufptr = save_string(bufptr, "%+");
@@ -779,26 +888,46 @@
 		bufptr = save_char(bufptr, '%');
 		ch1 = 0;
 		ch2 = 0;
+		digits = 0;
 		while (isdigit(UChar(*str))) {
+		    if (++digits > 2) {
+			syntax_error = TRUE;
+			break;
+		    }
 		    ch2 = ch1;
 		    ch1 = *str++;
-		    if (_nc_strict_bsd) {
-			if (ch1 > '3')
-			    return 0;
+		    if (digits == 2 && ch2 != '0') {
+			syntax_error = TRUE;
+			break;
+		    } else if (_nc_strict_bsd) {
+			if (ch1 > '3') {
+			    syntax_error = TRUE;
+			    break;
+			}
 		    } else {
 			bufptr = save_char(bufptr, ch1);
 		    }
 		}
+		if (syntax_error)
+		    break;
+		/*
+		 * Convert %02 to %2 and %03 to %3
+		 */
+		if (ch2 == '0' && !_nc_strict_bsd) {
+		    ch2 = 0;
+		    bufptr[-2] = bufptr[-1];
+		    *--bufptr = 0;
+		}
 		if (_nc_strict_bsd) {
-		    if (ch2 != 0 && ch2 != '0')
-			return 0;
-		    if (ch1 < '2')
+		    if (ch2 != 0 && ch2 != '0') {
+			syntax_error = TRUE;
+		    } else if (ch1 < '2') {
 			ch1 = 'd';
+		    }
 		    bufptr = save_char(bufptr, ch1);
 		}
-		if (strchr("doxX.", *str)) {
-		    if (*str != 'd')	/* termcap doesn't have octal, hex */
-			return 0;
+		if (strchr("oxX.", *str)) {
+		    syntax_error = TRUE;	/* termcap doesn't have octal, hex */
 		}
 		break;
 
@@ -811,14 +940,16 @@
 		break;
 
 		/*
-		 * %s isn't in termcap, but it's convenient to pass it through
+		 * %s isn't in termcap, but it is convenient to pass it through
 		 * so we can represent things like terminfo pfkey strings in
 		 * termcap notation.
 		 */
 	    case 's':
-		if (_nc_strict_bsd)
-		    return 0;
-		bufptr = save_string(bufptr, "%s");
+		if (_nc_strict_bsd) {
+		    syntax_error = TRUE;
+		} else {
+		    bufptr = save_string(bufptr, "%s");
+		}
 		break;
 
 	    case 'p':
@@ -830,8 +961,9 @@
 			bufptr = save_string(bufptr, "%r");
 			seentwo++;
 		    }
-		} else if (*str >= '3')
-		    return (0);
+		} else if (*str >= '3') {
+		    syntax_error = TRUE;
+		}
 		break;
 
 	    case 'i':
@@ -855,6 +987,24 @@
 
     }				/* endwhile (*str) */
 
+    if (!syntax_error &&
+	myfix > 0 &&
+	((int) strlen(my_string) - (4 * myfix)) < MIN_TC_FIXUPS) {
+	while (--myfix >= 0) {
+	    char *p = fixups[myfix].offset + my_string;
+	    *p++ = '^';
+	    *p++ = (char) (fixups[myfix].ch | '@');
+	    while ((p[0] = p[2]) != '\0') {
+		++p;
+	    }
+	}
+    }
+
+    DEBUG_THIS(("... _nc_infotocap %s",
+		syntax_error
+		? "<ERR>"
+		: _nc_visbuf(my_string)));
+
     return (syntax_error ? NULL : my_string);
 }
 
diff --git a/ncurses/tinfo/comp_error.c b/ncurses/tinfo/comp_error.c
index ff0acc7..3e6b402 100644
--- a/ncurses/tinfo/comp_error.c
+++ b/ncurses/tinfo/comp_error.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2019-2020,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2012,2016 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            *
@@ -41,7 +42,7 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: comp_error.c,v 1.36 2012/02/22 22:34:31 tom Exp $")
+MODULE_ID("$Id: comp_error.c,v 1.44 2023/06/15 20:27:02 tom Exp $")
 
 NCURSES_EXPORT_VAR(bool) _nc_suppress_warnings = FALSE;
 NCURSES_EXPORT_VAR(int) _nc_curr_line = 0; /* current line # in input */
@@ -59,19 +60,28 @@
 NCURSES_EXPORT(void)
 _nc_set_source(const char *const name)
 {
-    FreeIfNeeded(SourceName);
-    SourceName = strdup(name);
+    if (name == NULL) {
+	free(SourceName);
+	SourceName = NULL;
+    } else if (SourceName == NULL) {
+	SourceName = strdup(name);
+    } else if (strcmp(name, SourceName)) {
+	free(SourceName);
+	SourceName = strdup(name);
+    }
 }
 
 NCURSES_EXPORT(void)
 _nc_set_type(const char *const name)
 {
+#define MY_SIZE (size_t) MAX_NAME_SIZE
     if (TermType == 0)
-	TermType = typeMalloc(char, MAX_NAME_SIZE + 1);
+	TermType = typeMalloc(char, MY_SIZE + 1);
     if (TermType != 0) {
 	TermType[0] = '\0';
-	if (name)
-	    strncat(TermType, name, (size_t) MAX_NAME_SIZE);
+	if (name) {
+	    _nc_STRNCAT(TermType, name, MY_SIZE, MY_SIZE);
+	}
     }
 }
 
@@ -92,9 +102,9 @@
 where_is_problem(void)
 {
     fprintf(stderr, "\"%s\"", SourceName ? SourceName : "?");
-    if (_nc_curr_line >= 0)
+    if (_nc_curr_line > 0)
 	fprintf(stderr, ", line %d", _nc_curr_line);
-    if (_nc_curr_col >= 0)
+    if (_nc_curr_col > 0)
 	fprintf(stderr, ", col %d", _nc_curr_col);
     if (TermType != 0 && TermType[0] != '\0')
 	fprintf(stderr, ", terminal '%s'", TermType);
@@ -103,7 +113,7 @@
 }
 
 NCURSES_EXPORT(void)
-_nc_warning(const char *const fmt,...)
+_nc_warning(const char *const fmt, ...)
 {
     va_list argp;
 
@@ -118,7 +128,7 @@
 }
 
 NCURSES_EXPORT(void)
-_nc_err_abort(const char *const fmt,...)
+_nc_err_abort(const char *const fmt, ...)
 {
     va_list argp;
 
@@ -131,7 +141,7 @@
 }
 
 NCURSES_EXPORT(void)
-_nc_syserr_abort(const char *const fmt,...)
+_nc_syserr_abort(const char *const fmt, ...)
 {
     va_list argp;
 
@@ -141,16 +151,16 @@
     fprintf(stderr, "\n");
     va_end(argp);
 
+#if defined(TRACE) || !defined(NDEBUG)
     /* If we're debugging, try to show where the problem occurred - this
      * will dump core.
      */
-#if defined(TRACE) || !defined(NDEBUG)
-    abort();
-#else
+    if (_nc_env_access())
+	abort();
+#endif
     /* Dumping core in production code is not a good idea.
      */
     exit(EXIT_FAILURE);
-#endif
 }
 
 #if NO_LEAKS
diff --git a/ncurses/tinfo/comp_expand.c b/ncurses/tinfo/comp_expand.c
index 2ab06eb..e971384 100644
--- a/ncurses/tinfo/comp_expand.c
+++ b/ncurses/tinfo/comp_expand.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020-2021,2023 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            *
@@ -27,7 +28,7 @@
  ****************************************************************************/
 
 /****************************************************************************
- *  Author: Thomas E. Dickey <dickey@clark.net> 1998                        *
+ *  Author: Thomas E. Dickey                    1998                        *
  ****************************************************************************/
 
 #include <curses.priv.h>
@@ -35,7 +36,13 @@
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: comp_expand.c,v 1.25 2012/03/24 18:37:17 tom Exp $")
+MODULE_ID("$Id: comp_expand.c,v 1.35 2023/04/28 20:59:06 tom Exp $")
+
+#if 0
+#define DEBUG_THIS(p) DEBUG(9, p)
+#else
+#define DEBUG_THIS(p)		/* nothing */
+#endif
 
 static int
 trailing_spaces(const char *src)
@@ -46,10 +53,9 @@
 }
 
 /* this deals with differences over whether 0x7f and 0x80..0x9f are controls */
-#define REALCTL(s) (UChar(*(s)) < 127 && iscntrl(UChar(*(s))))
 #define REALPRINT(s) (UChar(*(s)) < 127 && isprint(UChar(*(s))))
 
-#define P_LIMIT(p) (length - (size_t)(p))
+#define P_LIMIT(p)   (length - (size_t)(p))
 
 NCURSES_EXPORT(char *)
 _nc_tic_expand(const char *srcp, bool tic_format, int numbers)
@@ -59,9 +65,13 @@
 
     int bufp;
     const char *str = VALID_STRING(srcp) ? srcp : "\0\0";
-    bool islong = (strlen(str) > 3);
     size_t need = (2 + strlen(str)) * 4;
     int ch;
+    int octals = 0;
+    struct {
+	int ch;
+	int offset;
+    } fixups[MAX_TC_FIXUPS];
 
     if (srcp == 0) {
 #if NO_LEAKS
@@ -77,6 +87,10 @@
 	      return 0;
     }
 
+    DEBUG_THIS(("_nc_tic_expand %s:%s:%s",
+		tic_format ? "ti" : "tc",
+		numbers ? "#" : "",
+		_nc_visbuf(srcp)));
     bufp = 0;
     while ((ch = UChar(*str)) != 0) {
 	if (ch == '%' && REALPRINT(str + 1)) {
@@ -115,7 +129,6 @@
 		    if (dst != 0
 			&& *dst == R_BRACE
 			&& value < 127
-			&& value != '\\'	/* FIXME */
 			&& isprint((int) value)) {
 			ch = (int) value;
 			buffer[bufp++] = S_QUOTE;
@@ -133,6 +146,8 @@
 		}
 		break;
 	    default:
+		if (*str == ',')	/* minitel1 uses this */
+		    buffer[bufp++] = '\\';
 		buffer[bufp++] = *str;
 		break;
 	    }
@@ -149,43 +164,37 @@
 					       trailing_spaces(str))) {
 	    buffer[bufp++] = '\\';
 	    buffer[bufp++] = 's';
-	} else if ((ch == ',' || ch == ':' || ch == '^') && tic_format) {
+	} else if ((ch == ',' || ch == '^') && tic_format) {
 	    buffer[bufp++] = '\\';
 	    buffer[bufp++] = (char) ch;
 	} else if (REALPRINT(str)
 		   && (ch != ','
-		       && ch != ':'
+		       && !(ch == ':' && !tic_format)
 		       && !(ch == '!' && !tic_format)
 		       && ch != '^'))
 	    buffer[bufp++] = (char) ch;
-#if 0				/* FIXME: this would be more readable (in fact the whole 'islong' logic should be removed) */
-	else if (ch == '\b') {
-	    buffer[bufp++] = '\\';
-	    buffer[bufp++] = 'b';
-	} else if (ch == '\f') {
-	    buffer[bufp++] = '\\';
-	    buffer[bufp++] = 'f';
-	} else if (ch == '\t' && islong) {
-	    buffer[bufp++] = '\\';
-	    buffer[bufp++] = 't';
-	}
-#endif
-	else if (ch == '\r' && (islong || (strlen(srcp) > 2 && str[1] == '\0'))) {
+	else if (ch == '\r') {
 	    buffer[bufp++] = '\\';
 	    buffer[bufp++] = 'r';
-	} else if (ch == '\n' && islong) {
+	} else if (ch == '\n') {
 	    buffer[bufp++] = '\\';
 	    buffer[bufp++] = 'n';
 	}
 #define UnCtl(c) ((c) + '@')
-	else if (REALCTL(str) && ch != '\\'
-		 && (!islong || isdigit(UChar(str[1])))) {
+	else if (UChar(ch) < 32
+		 && isdigit(UChar(str[1]))) {
 	    _nc_SPRINTF(&buffer[bufp], _nc_SLIMIT(P_LIMIT(bufp))
 			"^%c", UnCtl(ch));
 	    bufp += 2;
 	} else {
 	    _nc_SPRINTF(&buffer[bufp], _nc_SLIMIT(P_LIMIT(bufp))
 			"\\%03o", ch);
+	    if ((octals < MAX_TC_FIXUPS) &&
+		((tic_format && (ch == 127)) || ch < 32)) {
+		fixups[octals].ch = UChar(ch);
+		fixups[octals].offset = bufp;
+		++octals;
+	    }
 	    bufp += 4;
 	}
 
@@ -193,5 +202,26 @@
     }
 
     buffer[bufp] = '\0';
+
+    /*
+     * If most of a short string is ASCII control characters, reformat the
+     * string to show those in up-arrow format.  For longer strings, it is
+     * more likely that the characters are just binary coding.
+     *
+     * If we're formatting termcap, just use the shorter format (up-arrows).
+     */
+    if (octals != 0 && (!tic_format || (bufp - (4 * octals)) < MIN_TC_FIXUPS)) {
+	while (--octals >= 0) {
+	    char *p = buffer + fixups[octals].offset;
+	    *p++ = '^';
+	    *p++ = (char) ((fixups[octals].ch == 127)
+			   ? '?'
+			   : (fixups[octals].ch + (int) '@'));
+	    while ((p[0] = p[2]) != 0) {
+		++p;
+	    }
+	}
+    }
+    DEBUG_THIS(("... %s", _nc_visbuf(buffer)));
     return (buffer);
 }
diff --git a/ncurses/tinfo/comp_hash.c b/ncurses/tinfo/comp_hash.c
index 959c6e1..4b081af 100644
--- a/ncurses/tinfo/comp_hash.c
+++ b/ncurses/tinfo/comp_hash.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2008,2009 Free Software Foundation, Inc.              *
+ * Copyright 2019-2020,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2008,2009 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            *
@@ -44,13 +45,12 @@
 #include <tic.h>
 #include <hashsize.h>
 
-MODULE_ID("$Id: comp_hash.c,v 1.48 2009/08/08 17:36:21 tom Exp $")
+MODULE_ID("$Id: comp_hash.c,v 1.55 2023/06/24 13:29:43 tom Exp $")
 
 /*
  * Finds the entry for the given string in the hash table if present.
  * Returns a pointer to the entry in the table or 0 if not found.
  */
-/* entrypoint used by tack (do not alter) */
 NCURSES_EXPORT(struct name_table_entry const *)
 _nc_find_entry(const char *string,
 	       const HashValue * hash_table)
@@ -63,7 +63,9 @@
 
     hashvalue = data->hash_of(string);
 
-    if (data->table_data[hashvalue] >= 0) {
+    if (hashvalue >= 0
+	&& (unsigned) hashvalue < data->table_size
+	&& data->table_data[hashvalue] >= 0) {
 
 	real_table = _nc_get_table(termcap);
 	ptr = real_table + data->table_data[hashvalue];
@@ -96,19 +98,54 @@
     const HashData *data = _nc_get_hash_info(termcap);
     int hashvalue = data->hash_of(string);
 
-    if (data->table_data[hashvalue] >= 0) {
+    if (hashvalue >= 0
+	&& (unsigned) hashvalue < data->table_size
+	&& data->table_data[hashvalue] >= 0) {
 	const struct name_table_entry *const table = _nc_get_table(termcap);
 
-	ptr = table + data->table_data[hashvalue];
-	while (ptr->nte_type != type
-	       || !data->compare_names(ptr->nte_name, string)) {
-	    if (ptr->nte_link < 0) {
-		ptr = 0;
-		break;
+	if (table != NULL) {
+	    ptr = table + data->table_data[hashvalue];
+	    while (ptr->nte_type != type
+		   || !data->compare_names(ptr->nte_name, string)) {
+		if (ptr->nte_link < 0) {
+		    ptr = 0;
+		    break;
+		}
+		ptr = table + (ptr->nte_link + data->table_data[data->table_size]);
 	    }
-	    ptr = table + (ptr->nte_link + data->table_data[data->table_size]);
 	}
     }
 
     return ptr;
 }
+
+#if NCURSES_XNAMES
+NCURSES_EXPORT(struct user_table_entry const *)
+_nc_find_user_entry(const char *string)
+{
+    const HashData *data = _nc_get_hash_user();
+    int hashvalue;
+    struct user_table_entry const *ptr = 0;
+    struct user_table_entry const *real_table;
+
+    hashvalue = data->hash_of(string);
+
+    if (hashvalue >= 0
+	&& (unsigned) hashvalue < data->table_size
+	&& data->table_data[hashvalue] >= 0) {
+
+	real_table = _nc_get_userdefs_table();
+	ptr = real_table + data->table_data[hashvalue];
+	while (!data->compare_names(ptr->ute_name, string)) {
+	    if (ptr->ute_link < 0) {
+		ptr = 0;
+		break;
+	    }
+	    ptr = real_table + (ptr->ute_link
+				+ data->table_data[data->table_size]);
+	}
+    }
+
+    return (ptr);
+}
+#endif /* NCURSES_XNAMES */
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
diff --git a/ncurses/tinfo/comp_scan.c b/ncurses/tinfo/comp_scan.c
index fe6e8e7..3ba0835 100644
--- a/ncurses/tinfo/comp_scan.c
+++ b/ncurses/tinfo/comp_scan.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+,* Copyright 2020-2022,2023 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            *
@@ -50,7 +51,7 @@
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: comp_scan.c,v 1.102 2013/11/16 19:57:50 tom Exp $")
+MODULE_ID("$Id: comp_scan.c,v 1.122 2023/05/27 20:13:10 tom Exp $")
 
 /*
  * Maximum length of string capability we'll accept before raising an error.
@@ -111,6 +112,9 @@
 NCURSES_EXPORT(void)
 _nc_reset_input(FILE *fp, char *buf)
 {
+    TR(TRACE_DATABASE,
+       (T_CALLED("_nc_reset_input(fp=%p, buf=%p)"), (void *) fp, buf));
+
     pushtype = NO_PUSHBACK;
     if (pushname != 0)
 	pushname[0] = '\0';
@@ -120,6 +124,8 @@
     if (fp != 0)
 	_nc_curr_line = 0;
     _nc_curr_col = 0;
+
+    returnVoidDB;
 }
 
 /*
@@ -135,7 +141,7 @@
 
     while (len--) {
 	if (!isspace(UChar(bufptr[len]))) {
-	    if (from_end < (int) len)
+	    if (from_end <= (int) len)
 		result = bufptr[(int) len - from_end];
 	    break;
 	}
@@ -144,6 +150,32 @@
 }
 
 /*
+ * Read, like fgets(), but error-out if the input contains nulls.
+ */
+static int
+get_text(char *buffer, int length)
+{
+    int count = 0;
+    int limit = length - 1;
+
+    while (limit-- > 0) {
+	int ch = fgetc(yyin);
+
+	if (ch == '\0') {
+	    _nc_err_abort("This is not a text-file");
+	} else if (ch == EOF) {
+	    break;
+	}
+	++count;
+	*buffer++ = (char) ch;
+	if (ch == '\n')
+	    break;
+    }
+    *buffer = '\0';
+    return count;
+}
+
+/*
  *	int next_char()
  *
  *	Returns the next character in the input stream.  Comments and leading
@@ -168,6 +200,8 @@
 	if (result != 0) {
 	    FreeAndNull(result);
 	    FreeAndNull(pushname);
+	    bufptr = 0;
+	    bufstart = 0;
 	    allocated = 0;
 	}
 	/*
@@ -189,12 +223,11 @@
 	 * 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 {
+	    size_t used = 0;
 	    bufstart = 0;
-	    used = 0;
 	    do {
 		if (used + (LEXBUFSIZ / 4) >= allocated) {
 		    allocated += (allocated + LEXBUFSIZ);
@@ -207,7 +240,7 @@
 		if (used == 0)
 		    _nc_curr_file_pos = ftell(yyin);
 
-		if (fgets(result + used, (int) (allocated - used), yyin) != 0) {
+		if (get_text(result + used, (int) (allocated - used))) {
 		    bufstart = result;
 		    if (used == 0) {
 			if (_nc_curr_line == 0
@@ -223,6 +256,8 @@
 		}
 		if ((bufptr = bufstart) != 0) {
 		    used = strlen(bufptr);
+		    if (used == 0)
+			return (EOF);
 		    while (iswhite(*bufptr)) {
 			if (*bufptr == '\t') {
 			    _nc_curr_col = (_nc_curr_col | 7) + 1;
@@ -267,7 +302,7 @@
 /* push a character back onto the input stream */
 {
     if (bufptr == bufstart)
-	_nc_syserr_abort("Can't backspace off beginning of line");
+	_nc_syserr_abort("cannot backspace off beginning of line");
     *--bufptr = (char) c;
     _nc_curr_col--;
 }
@@ -276,14 +311,16 @@
 stream_pos(void)
 /* return our current character position in the input stream */
 {
-    return (yyin ? ftell(yyin) : (bufptr ? bufptr - bufstart : 0));
+    return (yyin ? ftell(yyin) : (bufptr ? (long) (bufptr - bufstart) : 0));
 }
 
 static bool
 end_of_stream(void)
 /* are we at end of input? */
 {
-    return ((yyin ? feof(yyin) : (bufptr && *bufptr == '\0'))
+    return ((yyin
+	     ? (feof(yyin) && (bufptr == NULL || *bufptr == '\0'))
+	     : (bufptr && *bufptr == '\0'))
 	    ? TRUE : FALSE);
 }
 
@@ -291,9 +328,11 @@
 static NCURSES_INLINE int
 eat_escaped_newline(int ch)
 {
-    if (ch == '\\')
-	while ((ch = next_char()) == '\n' || iswhite(ch))
-	    continue;
+    if (ch == '\\') {
+	while ((ch = next_char()) == '\n' || iswhite(ch)) {
+	    /* EMPTY */ ;
+	}
+    }
     return ch;
 }
 
@@ -363,6 +402,8 @@
     int old_col;
 #endif
 
+    DEBUG(3, (T_CALLED("_nc_get_token(silent=%d)"), silent));
+
     if (pushtype != NO_PUSHBACK) {
 	int retval = pushtype;
 
@@ -375,6 +416,7 @@
 	    pushname[0] = '\0';
 
 	/* currtok wasn't altered by _nc_push_token() */
+	DEBUG(3, (T_RETURN("%d"), retval));
 	return (retval);
     }
 
@@ -385,6 +427,7 @@
 	    if (_nc_curr_token.tk_name == tok_buf)
 		_nc_curr_token.tk_name = 0;
 	}
+	DEBUG(3, (T_RETURN("%d"), EOF));
 	return (EOF);
     }
 
@@ -393,7 +436,6 @@
     while ((ch = next_char()) == '\n' || iswhite(ch)) {
 	if (ch == '\n')
 	    had_newline = TRUE;
-	continue;
     }
 
     ch = eat_escaped_newline(ch);
@@ -418,8 +460,9 @@
 	    dot_flag = TRUE;
 	    DEBUG(8, ("dot-flag set"));
 
-	    while ((ch = next_char()) == '.' || iswhite(ch))
-		continue;
+	    while ((ch = next_char()) == '.' || iswhite(ch)) {
+		/* EMPTY */ ;
+	    }
 	}
 
 	if (ch == EOF) {
@@ -552,7 +595,7 @@
 		 * 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.
+		 * it is termcap syntax.
 		 */
 		_nc_syntax = SYN_TERMCAP;
 		separator = ':';
@@ -560,8 +603,9 @@
 		/* throw away trailing /, *$/ */
 		for (--tok_ptr;
 		     iswhite(*tok_ptr) || *tok_ptr == ',';
-		     tok_ptr--)
-		    continue;
+		     tok_ptr--) {
+		    /* EMPTY */ ;
+		}
 		tok_ptr[1] = '\0';
 	    }
 
@@ -583,14 +627,15 @@
 	     */
 	    if (after_list != 0) {
 		if (!silent) {
-		    if (*after_list == '\0')
+		    if (*after_list == '\0' || strchr("|", after_list[1]) != NULL) {
 			_nc_warning("empty longname field");
-		    else if (strchr(after_list, ' ') == 0)
+		    } else if (strchr(after_list, ' ') == 0) {
 			_nc_warning("older tic versions may treat the description field as an alias");
+		    }
 		}
 	    } else {
 		after_list = tok_buf + strlen(tok_buf);
-		DEBUG(1, ("missing description"));
+		DEBUG(2, ("missing description"));
 	    }
 
 	    /*
@@ -669,7 +714,15 @@
 		    if (numchk == numbuf)
 			_nc_warning("no value given for `%s'", tok_buf);
 		    if ((*numchk != '\0') || (ch != separator))
-			_nc_warning("Missing separator");
+			_nc_warning("Missing separator for `%s'", tok_buf);
+		    if (number < 0)
+			_nc_warning("value of `%s' cannot be negative", tok_buf);
+		    if (number > MAX_OF_TYPE(NCURSES_INT2)) {
+			_nc_warning("limiting value of `%s' from %#lx to %#x",
+				    tok_buf,
+				    number, MAX_OF_TYPE(NCURSES_INT2));
+			number = MAX_OF_TYPE(NCURSES_INT2);
+		    }
 		}
 		_nc_curr_token.tk_name = tok_buf;
 		_nc_curr_token.tk_valnumber = (int) number;
@@ -757,6 +810,7 @@
 	       : "<null>"),
 	      type));
 
+    DEBUG(3, (T_RETURN("%d"), type));
     return (type);
 }
 
@@ -780,7 +834,7 @@
  */
 
 NCURSES_EXPORT(int)
-_nc_trans_string(char *ptr, char *last)
+_nc_trans_string(char *ptr, const char *const last)
 {
     int count = 0;
     int number = 0;
@@ -810,8 +864,6 @@
 	    }
 	    if (c == '?' && (_nc_syntax != SYN_TERMCAP)) {
 		*(ptr++) = '\177';
-		if (_nc_tracing)
-		    _nc_warning("Allow ^? as synonym for \\177");
 	    } else {
 		if ((c &= 037) == 0)
 		    c = 128;
@@ -824,8 +876,6 @@
 	    if (c == EOF)
 		_nc_err_abort(MSG_NO_INPUTS);
 
-#define isoctal(c) ((c) >= '0' && (c) <= '7')
-
 	    if (isoctal(c) || (strict_bsd && isdigit(c))) {
 		number = c - '0';
 		for (i = 0; i < 2; i++) {
@@ -990,10 +1040,8 @@
 NCURSES_EXPORT(void)
 _nc_panic_mode(char ch)
 {
-    int c;
-
     for (;;) {
-	c = next_char();
+	int c = next_char();
 	if (c == ch)
 	    return;
 	if (c == EOF)
diff --git a/ncurses/tinfo/db_iterator.c b/ncurses/tinfo/db_iterator.c
index 94a4082..db3872e 100644
--- a/ncurses/tinfo/db_iterator.c
+++ b/ncurses/tinfo/db_iterator.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2006-2013,2014 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 Thomas E. Dickey                                *
+ * Copyright 2006-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            *
@@ -43,7 +44,7 @@
 #include <hashed_db.h>
 #endif
 
-MODULE_ID("$Id: db_iterator.c,v 1.39 2014/11/01 14:47:00 tom Exp $")
+MODULE_ID("$Id: db_iterator.c,v 1.50 2023/06/24 21:52:32 tom Exp $")
 
 #define HaveTicDirectory _nc_globals.have_tic_directory
 #define KeepTicDirectory _nc_globals.keep_tic_directory
@@ -72,15 +73,18 @@
 {
     bool result = FALSE;
 
-    if (stat(name, sb) == 0
-	&& (S_ISDIR(sb->st_mode) || S_ISREG(sb->st_mode))) {
+    if (quick_prefix(name)) {
+	result = TRUE;
+    } else if (stat(name, sb) == 0
+	       && (S_ISDIR(sb->st_mode)
+		   || (S_ISREG(sb->st_mode) && sb->st_size))) {
 	result = TRUE;
     }
 #if USE_HASHED_DB
     else if (strlen(name) < PATH_MAX - sizeof(DBM_SUFFIX)) {
 	char temp[PATH_MAX];
 	_nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp)) "%s%s", name, DBM_SUFFIX);
-	if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode)) {
+	if (stat(temp, sb) == 0 && S_ISREG(sb->st_mode) && sb->st_size) {
 	    result = TRUE;
 	}
     }
@@ -89,6 +93,27 @@
 }
 
 /*
+ * Trim newlines (and backslashes preceding those) and tab characters to
+ * help simplify scripting of the quick-dump feature.  Leave spaces and
+ * other backslashes alone.
+ */
+static void
+trim_formatting(char *source)
+{
+    char *target = source;
+    char ch;
+
+    while ((ch = *source++) != '\0') {
+	if (ch == '\\' && *source == '\n')
+	    continue;
+	if (ch == '\n' || ch == '\t')
+	    continue;
+	*target++ = ch;
+    }
+    *target = '\0';
+}
+
+/*
  * Store the latest value of an environment variable in my_vars[] so we can
  * detect if one changes, invalidating the cached search-list.
  */
@@ -99,19 +124,21 @@
 
     if (which < dbdLAST) {
 	char *value;
+	char *cached_value = my_vars[which].value;
+	bool same_value;
 
-	if ((value = getenv(name)) == 0 || (value = strdup(value)) == 0) {
-	    ;
-	} else if (my_vars[which].name == 0 || strcmp(my_vars[which].name, name)) {
-	    FreeIfNeeded(my_vars[which].value);
-	    my_vars[which].name = name;
-	    my_vars[which].value = value;
-	    result = TRUE;
-	} else if ((my_vars[which].value != 0) ^ (value != 0)) {
-	    FreeIfNeeded(my_vars[which].value);
-	    my_vars[which].value = value;
-	    result = TRUE;
-	} else if (value != 0 && strcmp(value, my_vars[which].value)) {
+	if ((value = getenv(name)) != 0) {
+	    value = strdup(value);
+	}
+	same_value = ((value == 0 && cached_value == 0) ||
+		      (value != 0 &&
+		       cached_value != 0 &&
+		       strcmp(value, cached_value) == 0));
+
+	/* Set variable name to enable checks in cache_expired(). */
+	my_vars[which].name = name;
+
+	if (!same_value) {
 	    FreeIfNeeded(my_vars[which].value);
 	    my_vars[which].value = value;
 	    result = TRUE;
@@ -122,6 +149,7 @@
     return result;
 }
 
+#if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
 static char *
 cache_getenv(const char *name, DBDIRS which)
 {
@@ -133,6 +161,7 @@
     }
     return result;
 }
+#endif
 
 /*
  * The cache expires if at least a second has passed since the initial lookup,
@@ -173,6 +202,13 @@
     FreeAndNull(my_list);
 }
 
+static void
+update_tic_dir(const char *update)
+{
+    free((char *) TicDirectory);
+    TicDirectory = update;
+}
+
 /*
  * Record the "official" location of the terminfo directory, according to
  * the place where we're writing to, or the normal default, if not.
@@ -182,8 +218,9 @@
 {
     T(("_nc_tic_dir %s", NonNull(path)));
     if (!KeepTicDirectory) {
-	if (path != 0) {
-	    TicDirectory = path;
+	if (path != NULL) {
+	    if (path != TicDirectory)
+		update_tic_dir(strdup(path));
 	    HaveTicDirectory = TRUE;
 	} else if (HaveTicDirectory == 0) {
 	    if (use_terminfo_vars()) {
@@ -251,7 +288,7 @@
     *state = dbdTIC;
     *offset = 0;
 
-    T(("_nc_first_db"));
+    T((T_CALLED("_nc_first_db")));
 
     /* build a blob containing all of the strings we will use for a lookup
      * table.
@@ -260,7 +297,7 @@
 	size_t blobsize = 0;
 	const char *values[dbdLAST];
 	struct stat *my_stat;
-	int j, k;
+	int j;
 
 	if (cache_has_expired)
 	    free_cache();
@@ -330,10 +367,12 @@
 	    my_list = typeCalloc(char *, blobsize);
 	    my_stat = typeCalloc(struct stat, blobsize);
 	    if (my_list != 0 && my_stat != 0) {
-		k = 0;
+		int k = 0;
 		my_list[k++] = my_blob;
 		for (j = 0; my_blob[j] != '\0'; ++j) {
-		    if (my_blob[j] == NCURSES_PATHSEP) {
+		    if (my_blob[j] == NCURSES_PATHSEP
+			&& ((&my_blob[j] - my_list[k - 1]) != 3
+			    || !quick_prefix(my_list[k - 1]))) {
 			my_blob[j] = '\0';
 			my_list[k++] = &my_blob[j + 1];
 		    }
@@ -344,11 +383,16 @@
 		 */
 		for (j = 0; my_list[j] != 0; ++j) {
 #ifdef TERMINFO
-		    if (*my_list[j] == '\0')
-			my_list[j] = strdup(TERMINFO);
+		    if (*my_list[j] == '\0') {
+			char *my_copy = strdup(TERMINFO);
+			if (my_copy != 0)
+			    my_list[j] = my_copy;
+		    }
 #endif
+		    trim_formatting(my_list[j]);
 		    for (k = 0; k < j; ++k) {
 			if (!strcmp(my_list[j], my_list[k])) {
+			    T(("duplicate %s", my_list[j]));
 			    k = j - 1;
 			    while ((my_list[j] = my_list[j + 1]) != 0) {
 				++j;
@@ -377,6 +421,7 @@
 		    }
 #endif
 		    if (!found) {
+			T(("not found %s", my_list[j]));
 			k = j;
 			while ((my_list[k] = my_list[k + 1]) != 0) {
 			    ++k;
@@ -392,6 +437,7 @@
 	    free(my_stat);
 	}
     }
+    returnVoid;
 }
 
 #if NO_LEAKS
@@ -409,5 +455,6 @@
 	FreeIfNeeded(my_vars[which].value);
 	my_vars[which].value = 0;
     }
+    update_tic_dir(NULL);
 }
 #endif
diff --git a/ncurses/tinfo/doalloc.c b/ncurses/tinfo/doalloc.c
index 7c502b0..e3b1a2e 100644
--- a/ncurses/tinfo/doalloc.c
+++ b/ncurses/tinfo/doalloc.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2000,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020,2021 Thomas E. Dickey                                     *
+ * Copyright 1998-2002,2012 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            *
@@ -39,15 +40,18 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: doalloc.c,v 1.11 2012/11/03 19:27:41 tom Exp $")
+MODULE_ID("$Id: doalloc.c,v 1.14 2021/04/24 23:43:39 tom Exp $")
 
-NCURSES_EXPORT(void *)
+void *
 _nc_doalloc(void *oldp, size_t amount)
 {
     void *newp;
 
-    if (oldp != 0) {
-	if ((newp = realloc(oldp, amount)) == 0) {
+    if (oldp != NULL) {
+	if (amount == 0) {
+	    free(oldp);
+	    newp = NULL;
+	} else if ((newp = realloc(oldp, amount)) == 0) {
 	    free(oldp);
 	    errno = ENOMEM;	/* just in case 'free' reset */
 	}
diff --git a/ncurses/tinfo/entries.c b/ncurses/tinfo/entries.c
index e84033d..f47fd3f 100644
--- a/ncurses/tinfo/entries.c
+++ b/ncurses/tinfo/entries.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2006-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2019-2022,2023 Thomas E. Dickey                                *
+ * Copyright 2006-2012,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            *
@@ -37,7 +38,7 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: entries.c,v 1.21 2012/05/05 20:33:44 tom Exp $")
+MODULE_ID("$Id: entries.c,v 1.35 2023/05/27 20:13:10 tom Exp $")
 
 /****************************************************************************
  *
@@ -63,30 +64,8 @@
 NCURSES_EXPORT_VAR(ENTRY *) _nc_head = 0;
 NCURSES_EXPORT_VAR(ENTRY *) _nc_tail = 0;
 
-NCURSES_EXPORT(void)
-_nc_free_entry(ENTRY * headp, TERMTYPE *tterm)
-/* free the allocated storage consumed by the given list entry */
-{
-    ENTRY *ep;
-
-    if ((ep = _nc_delink_entry(headp, tterm)) != 0) {
-	free(ep);
-    }
-}
-
-NCURSES_EXPORT(void)
-_nc_free_entries(ENTRY * headp)
-/* free the allocated storage consumed by list entries */
-{
-    (void) headp;		/* unused - _nc_head is altered here! */
-
-    while (_nc_head != 0) {
-	_nc_free_termtype(&(_nc_head->tterm));
-    }
-}
-
-NCURSES_EXPORT(ENTRY *)
-_nc_delink_entry(ENTRY * headp, TERMTYPE *tterm)
+static ENTRY *
+_nc_delink_entry(ENTRY * headp, const TERMTYPE2 *const tterm)
 /* delink the allocated storage for the given list entry */
 {
     ENTRY *ep, *last;
@@ -112,22 +91,63 @@
 }
 
 NCURSES_EXPORT(void)
+_nc_free_entry(ENTRY * headp, TERMTYPE2 *tterm)
+/* free the allocated storage consumed by the given list entry */
+{
+    ENTRY *ep;
+
+    if ((ep = _nc_delink_entry(headp, tterm)) != 0) {
+	free(ep);
+    }
+}
+
+NCURSES_EXPORT(void)
+_nc_free_entries(ENTRY * headp)
+/* free the allocated storage consumed by list entries */
+{
+    (void) headp;		/* unused - _nc_head is altered here! */
+
+    while (_nc_head != 0) {
+	_nc_free_termtype2(&(_nc_head->tterm));
+    }
+}
+
+NCURSES_EXPORT(void)
 _nc_leaks_tinfo(void)
 {
 #if NO_LEAKS
     char *s;
 #endif
 
-    T((T_CALLED("_nc_free_tinfo()")));
+    T((T_CALLED("_nc_leaks_tinfo()")));
 #if NO_LEAKS
-    _nc_free_tparm();
+    _nc_globals.leak_checking = TRUE;
+    _nc_free_tparm(cur_term);
     _nc_tgetent_leaks();
 
+#ifdef USE_PTHREADS
+    /*
+     * Discard any prescreen data which is not used for the current screen.
+     */
+    _nc_lock_global(screen);
+    {
+	PRESCREEN_LIST *p;
+	pthread_t id = GetThreadID();
+	for (p = _nc_prescreen.allocated; p != 0; p = p->next) {
+	    if (p->id == id && p->sp != CURRENT_SCREEN) {
+		FreeAndNull(p->sp);
+	    }
+	}
+    }
+    _nc_unlock_global(screen);
+#endif
     if (TerminalOf(CURRENT_SCREEN) != 0) {
 	del_curterm(TerminalOf(CURRENT_SCREEN));
     }
+    _nc_forget_prescr();
 
     _nc_comp_captab_leaks();
+    _nc_comp_userdefs_leaks();
     _nc_free_entries(_nc_head);
     _nc_get_type(0);
     _nc_first_name(0);
@@ -144,7 +164,8 @@
 	free(s);
 
 #ifdef TRACE
-    trace(0);
+    T((T_RETURN("")));
+    curses_trace(0);
     _nc_trace_buf(-1, (size_t) 0);
 #endif
 
@@ -156,7 +177,18 @@
 NCURSES_EXPORT(void)
 _nc_free_tinfo(int code)
 {
+    T((T_CALLED("_nc_free_tinfo(%d)"), code));
     _nc_leaks_tinfo();
     exit(code);
 }
 #endif
+
+NCURSES_EXPORT(void)
+exit_terminfo(int code)
+{
+    T((T_CALLED("exit_terminfo(%d)"), code));
+#if NO_LEAKS
+    _nc_leaks_tinfo();
+#endif
+    exit(code);
+}
diff --git a/ncurses/tinfo/free_ttype.c b/ncurses/tinfo/free_ttype.c
index ad056ba..7935152 100644
--- a/ncurses/tinfo/free_ttype.c
+++ b/ncurses/tinfo/free_ttype.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1999-2010,2011 Free Software Foundation, Inc.              *
+ * Copyright 2020-2022,2023 Thomas E. Dickey                                *
+ * Copyright 1999-2011,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            *
@@ -42,25 +43,52 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: free_ttype.c,v 1.15 2011/02/06 01:08:31 tom Exp $")
+MODULE_ID("$Id: free_ttype.c,v 1.22 2023/04/22 15:12:57 tom Exp $")
 
-NCURSES_EXPORT(void)
-_nc_free_termtype(TERMTYPE *ptr)
+static void
+really_free_termtype(TERMTYPE2 *ptr, bool freeStrings)
 {
-    T(("_nc_free_termtype(%s)", ptr->term_names));
+    T(("really_free_termtype(%s) %d", ptr->term_names, freeStrings));
 
-    FreeIfNeeded(ptr->str_table);
+    if (freeStrings) {
+	FreeIfNeeded(ptr->str_table);
+    }
     FreeIfNeeded(ptr->Booleans);
     FreeIfNeeded(ptr->Numbers);
     FreeIfNeeded(ptr->Strings);
 #if NCURSES_XNAMES
-    FreeIfNeeded(ptr->ext_str_table);
+    if (freeStrings) {
+	FreeIfNeeded(ptr->ext_str_table);
+    }
     FreeIfNeeded(ptr->ext_Names);
 #endif
     memset(ptr, 0, sizeof(TERMTYPE));
     _nc_free_entry(_nc_head, ptr);
 }
 
+NCURSES_EXPORT(void)
+_nc_free_termtype(TERMTYPE *ptr)
+{
+    really_free_termtype((TERMTYPE2 *) ptr, !NCURSES_EXT_NUMBERS);
+}
+
+/*
+ * These similar entrypoints are not used outside of ncurses.
+ */
+NCURSES_EXPORT(void)
+_nc_free_termtype1(TERMTYPE *ptr)
+{
+    really_free_termtype((TERMTYPE2 *) ptr, TRUE);
+}
+
+#if NCURSES_EXT_NUMBERS
+NCURSES_EXPORT(void)
+_nc_free_termtype2(TERMTYPE2 *ptr)
+{
+    really_free_termtype(ptr, TRUE);
+}
+#endif
+
 #if NCURSES_XNAMES
 NCURSES_EXPORT_VAR(bool) _nc_user_definable = TRUE;
 
diff --git a/ncurses/tinfo/getenv_num.c b/ncurses/tinfo/getenv_num.c
index d5e35cb..ca179d3 100644
--- a/ncurses/tinfo/getenv_num.c
+++ b/ncurses/tinfo/getenv_num.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018,2020 Thomas E. Dickey                                     *
+ * Copyright 1998-2012,2013 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            *
@@ -36,7 +37,7 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: getenv_num.c,v 1.6 2013/09/28 20:25:08 tom Exp $")
+MODULE_ID("$Id: getenv_num.c,v 1.8 2020/02/02 23:34:34 tom Exp $")
 
 NCURSES_EXPORT(int)
 _nc_getenv_num(const char *name)
@@ -68,6 +69,8 @@
 	_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) "%s=%d", name, value);
 	if ((s = strdup(buffer)) != 0)
 	    putenv(s);
+#else
+#error expected setenv/putenv functions
 #endif
     }
 }
diff --git a/ncurses/tinfo/hashed_db.c b/ncurses/tinfo/hashed_db.c
index b594205..b78d98f 100644
--- a/ncurses/tinfo/hashed_db.c
+++ b/ncurses/tinfo/hashed_db.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2006-2011,2013 Free Software Foundation, Inc.              *
+ * Copyright 2019,2020 Thomas E. Dickey                                     *
+ * Copyright 2006-2011,2013 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            *
@@ -36,7 +37,7 @@
 
 #if USE_HASHED_DB
 
-MODULE_ID("$Id: hashed_db.c,v 1.17 2013/12/15 00:33:01 tom Exp $")
+MODULE_ID("$Id: hashed_db.c,v 1.19 2020/02/02 23:34:34 tom Exp $")
 
 #if HASHED_DB_API >= 2
 static DBC *cursor;
@@ -273,7 +274,7 @@
 	result = -1;
     }
 #else
-    result = db->seq(db, key, data, 0);
+    result = db->seq(db, key, data, R_NEXT);
 #endif
     return result;
 }
diff --git a/ncurses/tinfo/home_terminfo.c b/ncurses/tinfo/home_terminfo.c
index e77f71c..7e626df 100644
--- a/ncurses/tinfo/home_terminfo.c
+++ b/ncurses/tinfo/home_terminfo.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2010,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020 Thomas E. Dickey                                          *
+ * Copyright 1998-2012,2016 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            *
@@ -37,7 +38,7 @@
 #include <curses.priv.h>
 #include <tic.h>
 
-MODULE_ID("$Id: home_terminfo.c,v 1.15 2012/10/27 21:49:14 tom Exp $")
+MODULE_ID("$Id: home_terminfo.c,v 1.17 2020/02/02 23:34:34 tom Exp $")
 
 /* ncurses extension...fall back on user's private directory */
 
@@ -48,10 +49,11 @@
 {
     char *result = 0;
 #if USE_HOME_TERMINFO
-    char *home;
-
     if (use_terminfo_vars()) {
+
 	if (MyBuffer == 0) {
+	    char *home;
+
 	    if ((home = getenv("HOME")) != 0) {
 		size_t want = (strlen(home) + sizeof(PRIVATE_INFO));
 		TYPE_MALLOC(char, want, MyBuffer);
diff --git a/ncurses/tinfo/init_keytry.c b/ncurses/tinfo/init_keytry.c
index ea47b38..ef20dfb 100644
--- a/ncurses/tinfo/init_keytry.c
+++ b/ncurses/tinfo/init_keytry.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1999-2009,2010 Free Software Foundation, Inc.              *
+ * Copyright 2020,2023 Thomas E. Dickey                                     *
+ * Copyright 1999-2010,2016 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            *
@@ -29,7 +30,7 @@
 #include <curses.priv.h>
 #include <tic.h>		/* struct tinfo_fkeys */
 
-MODULE_ID("$Id: init_keytry.c,v 1.17 2010/04/24 22:29:56 tom Exp $")
+MODULE_ID("$Id: init_keytry.c,v 1.20 2023/09/16 12:55:01 tom Exp $")
 
 /*
 **      _nc_init_keytry()
@@ -66,7 +67,7 @@
 NCURSES_EXPORT(void)
 _nc_init_keytry(SCREEN *sp)
 {
-    unsigned n;
+    T(("_nc_init_keytry(%p)", (void *) sp));
 
     /* The sp->_keytry value is initialized in newterm(), where the sp
      * structure is created, because we can not tell where keypad() or
@@ -74,6 +75,8 @@
      */
 
     if (sp != 0) {
+	unsigned n;
+
 	for (n = 0; _nc_tinfo_fkeys[n].code; n++) {
 	    if (_nc_tinfo_fkeys[n].offset < STRCOUNT) {
 		(void) _nc_add_to_try(&(sp->_keytry),
@@ -94,7 +97,7 @@
 		char *value = tp->Strings[n];
 		if (name != 0
 		    && *name == 'k'
-		    && value != 0
+		    && VALID_STRING(value)
 		    && NCURSES_SP_NAME(key_defined) (NCURSES_SP_ARGx
 						     value) == 0) {
 		    (void) _nc_add_to_try(&(sp->_keytry),
diff --git a/ncurses/tinfo/lib_acs.c b/ncurses/tinfo/lib_acs.c
index 69a1851..4ede53f 100644
--- a/ncurses/tinfo/lib_acs.c
+++ b/ncurses/tinfo/lib_acs.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc.              *
+ * Copyright 2018-2019,2020 Thomas E. Dickey                                *
+ * Copyright 1998-2014,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            *
@@ -39,7 +40,7 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_acs.c,v 1.45 2014/03/08 20:32:59 tom Exp $")
+MODULE_ID("$Id: lib_acs.c,v 1.50 2020/02/02 23:34:34 tom Exp $")
 
 #if BROKEN_LINKER || USE_REENTRANT
 #define MyBuffer _nc_prescreen.real_acs_map
@@ -171,7 +172,7 @@
     if (ena_acs != NULL) {
 	NCURSES_PUTP2("ena_acs", ena_acs);
     }
-#if NCURSES_EXT_FUNCS
+#if NCURSES_EXT_FUNCS && defined(enter_pc_charset_mode) && defined(exit_pc_charset_mode)
     /*
      * Linux console "supports" the "PC ROM" character set by the coincidence
      * that smpch/rmpch and smacs/rmacs have the same values.  ncurses has
@@ -205,8 +206,13 @@
 	while (i + 1 < length) {
 	    if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) {
 		real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET;
-		if (SP != 0)
+		T(("#%d real_map[%s] = %s",
+		   (int) i,
+		   _tracechar(UChar(acs_chars[i])),
+		   _tracechtype(real_map[UChar(acs_chars[i])])));
+		if (SP != 0) {
 		    SP->_screen_acs_map[UChar(acs_chars[i])] = TRUE;
+		}
 	    }
 	    i += 2;
 	}
@@ -249,3 +255,72 @@
     NCURSES_SP_NAME(_nc_init_acs) (CURRENT_SCREEN);
 }
 #endif
+
+#if !NCURSES_WCWIDTH_GRAPHICS
+NCURSES_EXPORT(int)
+_nc_wacs_width(unsigned ch)
+{
+    int result;
+    switch (ch) {
+    case 0x00a3:		/* FALLTHRU - ncurses pound-sterling symbol */
+    case 0x00b0:		/* FALLTHRU - VT100 degree symbol */
+    case 0x00b1:		/* FALLTHRU - VT100 plus/minus */
+    case 0x00b7:		/* FALLTHRU - VT100 bullet */
+    case 0x03c0:		/* FALLTHRU - ncurses greek pi */
+    case 0x2190:		/* FALLTHRU - Teletype arrow pointing left */
+    case 0x2191:		/* FALLTHRU - Teletype arrow pointing up */
+    case 0x2192:		/* FALLTHRU - Teletype arrow pointing right */
+    case 0x2193:		/* FALLTHRU - Teletype arrow pointing down */
+    case 0x2260:		/* FALLTHRU - ncurses not-equal */
+    case 0x2264:		/* FALLTHRU - ncurses less-than-or-equal-to */
+    case 0x2265:		/* FALLTHRU - ncurses greater-than-or-equal-to */
+    case 0x23ba:		/* FALLTHRU - VT100 scan line 1 */
+    case 0x23bb:		/* FALLTHRU - ncurses scan line 3 */
+    case 0x23bc:		/* FALLTHRU - ncurses scan line 7 */
+    case 0x23bd:		/* FALLTHRU - VT100 scan line 9 */
+    case 0x2500:		/* FALLTHRU - VT100 horizontal line */
+    case 0x2501:		/* FALLTHRU - thick horizontal line */
+    case 0x2502:		/* FALLTHRU - VT100 vertical line */
+    case 0x2503:		/* FALLTHRU - thick vertical line */
+    case 0x250c:		/* FALLTHRU - VT100 upper left corner */
+    case 0x250f:		/* FALLTHRU - thick upper left corner */
+    case 0x2510:		/* FALLTHRU - VT100 upper right corner */
+    case 0x2513:		/* FALLTHRU - thick upper right corner */
+    case 0x2514:		/* FALLTHRU - VT100 lower left corner */
+    case 0x2517:		/* FALLTHRU - thick lower left corner */
+    case 0x2518:		/* FALLTHRU - VT100 lower right corner */
+    case 0x251b:		/* FALLTHRU - thick lower right corner */
+    case 0x251c:		/* FALLTHRU - VT100 tee pointing left */
+    case 0x2523:		/* FALLTHRU - thick tee pointing left */
+    case 0x2524:		/* FALLTHRU - VT100 tee pointing right */
+    case 0x252b:		/* FALLTHRU - thick tee pointing right */
+    case 0x252c:		/* FALLTHRU - VT100 tee pointing down */
+    case 0x2533:		/* FALLTHRU - thick tee pointing down */
+    case 0x2534:		/* FALLTHRU - VT100 tee pointing up */
+    case 0x253b:		/* FALLTHRU - thick tee pointing up */
+    case 0x253c:		/* FALLTHRU - VT100 large plus or crossover */
+    case 0x254b:		/* FALLTHRU - thick large plus or crossover */
+    case 0x2550:		/* FALLTHRU - double horizontal line */
+    case 0x2551:		/* FALLTHRU - double vertical line */
+    case 0x2554:		/* FALLTHRU - double upper left corner */
+    case 0x2557:		/* FALLTHRU - double upper right corner */
+    case 0x255a:		/* FALLTHRU - double lower left corner */
+    case 0x255d:		/* FALLTHRU - double lower right corner */
+    case 0x2560:		/* FALLTHRU - double tee pointing right */
+    case 0x2563:		/* FALLTHRU - double tee pointing left */
+    case 0x2566:		/* FALLTHRU - double tee pointing down */
+    case 0x2569:		/* FALLTHRU - double tee pointing up */
+    case 0x256c:		/* FALLTHRU - double large plus or crossover */
+    case 0x2592:		/* FALLTHRU - VT100 checker board (stipple) */
+    case 0x25ae:		/* FALLTHRU - Teletype solid square block */
+    case 0x25c6:		/* FALLTHRU - VT100 diamond */
+    case 0x2603:		/* FALLTHRU - Teletype lantern symbol */
+	result = 1;
+	break;
+    default:
+	result = wcwidth(ch);
+	break;
+    }
+    return result;
+}
+#endif /* !NCURSES_WCWIDTH_GRAPHICS */
diff --git a/ncurses/tinfo/lib_baudrate.c b/ncurses/tinfo/lib_baudrate.c
index 1cf5505..311c41a 100644
--- a/ncurses/tinfo/lib_baudrate.c
+++ b/ncurses/tinfo/lib_baudrate.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc.              *
+ * Copyright 2020 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            *
@@ -39,7 +40,7 @@
 
 #include <curses.priv.h>
 #include <termcap.h>		/* ospeed */
-#if defined(__FreeBSD__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
 #include <sys/param.h>
 #endif
 
@@ -49,7 +50,11 @@
  * of the indices up to B115200 fit nicely in a 'short', allowing us to retain
  * ospeed's type for compatibility.
  */
-#if NCURSES_OSPEED_COMPAT && ((defined(__FreeBSD__) && (__FreeBSD_version < 700000)) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__))
+#if NCURSES_OSPEED_COMPAT && \
+ 	((defined(__FreeBSD__) && (__FreeBSD_version < 700000)) || \
+	defined(__NetBSD__) || \
+	((defined(__OpenBSD__) && OpenBSD < 201510)) || \
+	defined(__APPLE__))
 #undef B0
 #undef B50
 #undef B75
@@ -79,7 +84,7 @@
 #undef USE_OLD_TTY
 #endif /* USE_OLD_TTY */
 
-MODULE_ID("$Id: lib_baudrate.c,v 1.37 2015/06/14 00:34:12 tom Exp $")
+MODULE_ID("$Id: lib_baudrate.c,v 1.45 2020/09/05 21:15:32 tom Exp $")
 
 /*
  *	int
@@ -90,10 +95,11 @@
  */
 
 struct speed {
-    NCURSES_OSPEED s;		/* values for 'ospeed' */
-    int sp;			/* the actual speed */
+    int given_speed;		/* values for 'ospeed' */
+    int actual_speed;		/* the actual speed */
 };
 
+#if !defined(EXP_WIN32_DRIVER)
 #define DATA(number) { B##number, number }
 
 static struct speed const speeds[] =
@@ -117,6 +123,9 @@
 #elif defined(EXTA)
     {EXTA, 19200},
 #endif
+#ifdef B28800
+    DATA(28800),
+#endif
 #ifdef B38400
     DATA(38400),
 #elif defined(EXTB)
@@ -127,32 +136,80 @@
 #endif
     /* ifdef to prevent overflow when OLD_TTY is not available */
 #if !(NCURSES_OSPEED_COMPAT && defined(__FreeBSD__) && (__FreeBSD_version > 700000))
+#ifdef B76800
+    DATA(76800),
+#endif
 #ifdef B115200
     DATA(115200),
 #endif
+#ifdef B153600
+    DATA(153600),
+#endif
 #ifdef B230400
     DATA(230400),
 #endif
+#ifdef B307200
+    DATA(307200),
+#endif
 #ifdef B460800
     DATA(460800),
 #endif
+#ifdef B500000
+    DATA(500000),
+#endif
+#ifdef B576000
+    DATA(576000),
+#endif
 #ifdef B921600
     DATA(921600),
 #endif
+#ifdef B1000000
+    DATA(1000000),
+#endif
+#ifdef B1152000
+    DATA(1152000),
+#endif
+#ifdef B1500000
+    DATA(1500000),
+#endif
+#ifdef B2000000
+    DATA(2000000),
+#endif
+#ifdef B2500000
+    DATA(2500000),
+#endif
+#ifdef B3000000
+    DATA(3000000),
+#endif
+#ifdef B3500000
+    DATA(3500000),
+#endif
+#ifdef B4000000
+    DATA(4000000),
+#endif
 #endif
 };
+#endif /* !EXP_WIN32_DRIVER */
 
 NCURSES_EXPORT(int)
 _nc_baudrate(int OSpeed)
 {
+#if defined(EXP_WIN32_DRIVER)
+    /* On Windows this is a noop */
+    (void) OSpeed;
+    return (OK);
+#else
 #if !USE_REENTRANT
     static int last_OSpeed;
     static int last_baudrate;
 #endif
 
     int result = ERR;
-    unsigned i;
 
+    if (OSpeed < 0)
+	OSpeed = (NCURSES_OSPEED) OSpeed;
+    if (OSpeed < 0)
+	OSpeed = (unsigned short) OSpeed;
 #if !USE_REENTRANT
     if (OSpeed == last_OSpeed) {
 	result = last_baudrate;
@@ -160,9 +217,14 @@
 #endif
     if (result == ERR) {
 	if (OSpeed >= 0) {
+	    unsigned i;
+
 	    for (i = 0; i < SIZEOF(speeds); i++) {
-		if ((int) speeds[i].s == OSpeed) {
-		    result = speeds[i].sp;
+		if (speeds[i].given_speed > OSpeed) {
+		    break;
+		}
+		if (speeds[i].given_speed == OSpeed) {
+		    result = speeds[i].actual_speed;
 		    break;
 		}
 	    }
@@ -175,22 +237,27 @@
 #endif
     }
     return (result);
+#endif /* !EXP_WIN32_DRIVER */
 }
 
 NCURSES_EXPORT(int)
 _nc_ospeed(int BaudRate)
 {
     int result = 1;
-    unsigned i;
-
+#if defined(EXP_WIN32_DRIVER)
+    (void) BaudRate;
+#else
     if (BaudRate >= 0) {
+	unsigned i;
+
 	for (i = 0; i < SIZEOF(speeds); i++) {
-	    if (speeds[i].sp == BaudRate) {
-		result = speeds[i].s;
+	    if (speeds[i].actual_speed == BaudRate) {
+		result = speeds[i].given_speed;
 		break;
 	    }
 	}
     }
+#endif
     return (result);
 }
 
@@ -201,6 +268,9 @@
 
     T((T_CALLED("baudrate(%p)"), (void *) SP_PARM));
 
+#if defined(EXP_WIN32_DRIVER)
+    result = OK;
+#else
     /*
      * In debugging, allow the environment symbol to override when we're
      * redirecting to a file, so we can construct repeatable test-cases
@@ -208,7 +278,7 @@
      */
 #ifdef TRACE
     if (IsValidTIScreen(SP_PARM)
-	&& !NC_ISATTY(fileno(SP_PARM ? SP_PARM->_ofp : stdout))
+	&& !NC_ISATTY(fileno((SP_PARM && SP_PARM->_ofp) ? SP_PARM->_ofp : stdout))
 	&& getenv("BAUDRATE") != 0) {
 	int ret;
 	if ((ret = _nc_getenv_num("BAUDRATE")) <= 0)
@@ -234,7 +304,7 @@
     } else {
 	result = ERR;
     }
-
+#endif /* !EXP_WIN32_DRIVER */
     returnCode(result);
 }
 
diff --git a/ncurses/tinfo/lib_cur_term.c b/ncurses/tinfo/lib_cur_term.c
index 9941d13..1f8db2c 100644
--- a/ncurses/tinfo/lib_cur_term.c
+++ b/ncurses/tinfo/lib_cur_term.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc.              *
+,* Copyright 2020-2021,2022 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            *
@@ -38,11 +39,12 @@
 
 #include <curses.priv.h>
 #include <termcap.h>		/* ospeed */
+#include <tic.h>		/* VALID_STRING */
 
-MODULE_ID("$Id: lib_cur_term.c,v 1.33 2014/03/08 20:32:59 tom Exp $")
+MODULE_ID("$Id: lib_cur_term.c,v 1.49 2022/05/28 17:56:55 tom Exp $")
 
 #undef CUR
-#define CUR termp->type.
+#define CUR TerminalType(termp).
 
 #if USE_REENTRANT
 
@@ -76,7 +78,7 @@
 #endif
 
 NCURSES_EXPORT(TERMINAL *)
-NCURSES_SP_NAME(set_curterm) (NCURSES_SP_DCLx TERMINAL * termp)
+NCURSES_SP_NAME(set_curterm) (NCURSES_SP_DCLx TERMINAL *termp)
 {
     TERMINAL *oldterm;
 
@@ -95,16 +97,21 @@
 #ifdef USE_TERM_DRIVER
 	TERMINAL_CONTROL_BLOCK *TCB = (TERMINAL_CONTROL_BLOCK *) termp;
 	ospeed = (NCURSES_OSPEED) _nc_ospeed(termp->_baudrate);
-	if (TCB->drv->isTerminfo && termp->type.Strings) {
-	    PC = (char) ((pad_char != NULL) ? pad_char[0] : 0);
+	if (TCB->drv &&
+	    TCB->drv->isTerminfo &&
+	    TerminalType(termp).Strings) {
+	    PC = (char) (VALID_STRING(pad_char) ? pad_char[0] : 0);
 	}
 	TCB->csp = SP_PARM;
 #else
 	ospeed = (NCURSES_OSPEED) _nc_ospeed(termp->_baudrate);
-	if (termp->type.Strings) {
-	    PC = (char) ((pad_char != NULL) ? pad_char[0] : 0);
+	if (TerminalType(termp).Strings) {
+	    PC = (char) (VALID_STRING(pad_char) ? pad_char[0] : 0);
 	}
 #endif
+#if !USE_REENTRANT
+	save_ttytype(termp);
+#endif
     }
     _nc_unlock_global(curses);
 
@@ -114,14 +121,14 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(TERMINAL *)
-set_curterm(TERMINAL * termp)
+set_curterm(TERMINAL *termp)
 {
     return NCURSES_SP_NAME(set_curterm) (CURRENT_SCREEN, termp);
 }
 #endif
 
 NCURSES_EXPORT(int)
-NCURSES_SP_NAME(del_curterm) (NCURSES_SP_DCLx TERMINAL * termp)
+NCURSES_SP_NAME(del_curterm) (NCURSES_SP_DCLx TERMINAL *termp)
 {
     int rc = ERR;
 
@@ -139,7 +146,14 @@
 #endif
 	);
 
-	_nc_free_termtype(&(termp->type));
+#if NCURSES_EXT_NUMBERS
+#if NCURSES_EXT_COLORS
+	_nc_free_termtype1(&termp->type);
+#else
+	_nc_free_termtype2(&termp->type);
+#endif
+#endif
+	_nc_free_termtype2(&TerminalType(termp));
 	if (termp == cur)
 	    NCURSES_SP_NAME(set_curterm) (NCURSES_SP_ARGx 0);
 
@@ -153,18 +167,29 @@
 	if (TCB->drv)
 	    TCB->drv->td_release(TCB);
 #endif
+#if NO_LEAKS
+	/* discard memory used in tgetent's cache for this terminal */
+	_nc_tgetent_leak(termp);
+#endif
+	if (--_nc_globals.terminal_count == 0) {
+	    _nc_free_tparm(termp);
+	}
+
+	free(termp->tparm_state.fmt_buff);
+	free(termp->tparm_state.out_buff);
 	free(termp);
 
 	rc = OK;
     }
+
     returnCode(rc);
 }
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(int)
-del_curterm(TERMINAL * termp)
+del_curterm(TERMINAL *termp)
 {
-    int rc = ERR;
+    int rc;
 
     _nc_lock_global(curses);
     rc = NCURSES_SP_NAME(del_curterm) (CURRENT_SCREEN, termp);
diff --git a/ncurses/tinfo/lib_data.c b/ncurses/tinfo/lib_data.c
index 06b6f88..b817f49 100644
--- a/ncurses/tinfo/lib_data.c
+++ b/ncurses/tinfo/lib_data.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,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            *
@@ -42,7 +43,7 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_data.c,v 1.66 2013/08/24 17:28:24 tom Exp $")
+MODULE_ID("$Id: lib_data.c,v 1.89 2024/02/24 18:11:38 tom Exp $")
 
 /*
  * OS/2's native linker complains if we don't initialize public data when
@@ -94,7 +95,9 @@
 NCURSES_EXPORT(int)
 _nc_alloc_screen(void)
 {
-    return ((my_screen = _nc_alloc_screen_sp()) != 0);
+    my_screen = _nc_alloc_screen_sp();
+    T(("_nc_alloc_screen_sp %p", my_screen));
+    return (my_screen != 0);
 }
 
 NCURSES_EXPORT(void)
@@ -137,12 +140,15 @@
 
     0,				/* slk_format */
 
+    2048,			/* getstr_limit */
+
     NULL,			/* safeprint_buf */
     0,				/* safeprint_used */
 
     TGETENT_0s,			/* tgetent_cache */
     0,				/* tgetent_index */
     0,				/* tgetent_sequence */
+    0,				/* terminal_count */
 
     0,				/* dbd_blob */
     0,				/* dbd_list */
@@ -150,6 +156,15 @@
     0,				/* dbd_time */
     { { 0, 0 } },		/* dbd_vars */
 
+#if HAVE_TSEARCH
+    NULL,			/* cached_tparm */
+    0,				/* count_tparm */
+#endif /* HAVE_TSEARCH */
+
+#ifdef USE_TERM_DRIVER
+    0,				/* term_driver */
+#endif
+
 #ifndef USE_SP_WINDOWLIST
     0,				/* _nc_windowlist */
 #endif
@@ -163,15 +178,27 @@
     0,				/* safeprint_rows */
 #endif
 
-#ifdef USE_TERM_DRIVER
-    0,				/* term_driver */
+#ifdef USE_PTHREADS
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_curses */
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_prescreen */
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_screen */
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_update */
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_tst_tracef */
+    PTHREAD_MUTEX_INITIALIZER,	/* mutex_tracef */
+    0,				/* nested_tracef */
+    0,				/* use_pthreads */
+#if USE_PTHREADS_EINTR
+    0,				/* read_thread */
 #endif
-
+#endif
+#if USE_WIDEC_SUPPORT
+    CHARS_0s,			/* key_name */
+#endif
 #ifdef TRACE
-    FALSE,			/* init_trace */
-    CHARS_0s,			/* trace_fname */
+    FALSE,			/* trace_opened */
     0,				/* trace_level */
     NULL,			/* trace_fp */
+    -1,				/* trace_fd */
 
     NULL,			/* tracearg_buf */
     0,				/* tracearg_used */
@@ -194,15 +221,8 @@
     0,				/* nested_tracef */
 #endif
 #endif /* TRACE */
-#ifdef USE_PTHREADS
-    PTHREAD_MUTEX_INITIALIZER,	/* mutex_curses */
-    PTHREAD_MUTEX_INITIALIZER,	/* mutex_tst_tracef */
-    PTHREAD_MUTEX_INITIALIZER,	/* mutex_tracef */
-    0,				/* nested_tracef */
-    0,				/* use_pthreads */
-#endif
-#if USE_PTHREADS_EINTR
-    0,				/* read_thread */
+#if NO_LEAKS
+    FALSE,			/* leak_checking */
 #endif
 };
 
@@ -214,17 +234,11 @@
 #define RIPOFF_0s	{ RIPOFF_0 }
 
 NCURSES_EXPORT_VAR(NCURSES_PRESCREEN) _nc_prescreen = {
+    NULL,			/* allocated */
     TRUE,			/* use_env */
     FALSE,			/* filter_mode */
     A_NORMAL,			/* previous_attr */
-#ifndef USE_SP_RIPOFF
-    RIPOFF_0s,			/* ripoff */
-    NULL,			/* rsp */
-#endif
     {				/* tparm_state */
-#ifdef TRACE
-	NULL,			/* tname */
-#endif
 	NULL,			/* tparam_base */
 
 	STACK_FRAME_0s,		/* stack */
@@ -237,14 +251,21 @@
 	NULL,			/* fmt_buff */
 	0,			/* fmt_size */
 
-	NUM_VARS_0s,		/* dynamic_var */
 	NUM_VARS_0s,		/* static_vars */
+#ifdef TRACE
+	NULL,			/* tname */
+#endif
     },
     NULL,			/* saved_tty */
+    FALSE,			/* use_tioctl */
+    0,				/* _outch */
+#ifndef USE_SP_RIPOFF
+    RIPOFF_0s,			/* ripoff */
+    NULL,			/* rsp */
+#endif
 #if NCURSES_NO_PADDING
     FALSE,			/* flag to set if padding disabled  */
 #endif
-    0,				/* _outch */
 #if BROKEN_LINKER || USE_REENTRANT
     NULL,			/* real_acs_map */
     0,				/* LINES */
@@ -252,12 +273,13 @@
     8,				/* TABSIZE */
     1000,			/* ESCDELAY */
     0,				/* cur_term */
+#endif
 #ifdef TRACE
+#if BROKEN_LINKER || USE_REENTRANT
     0L,				/* _outchars */
     NULL,			/* _tputs_trace */
 #endif
 #endif
-    FALSE,			/* use_tioctl */
 };
 /* *INDENT-ON* */
 
@@ -287,6 +309,9 @@
     if (!initialized) {
 	initialized = TRUE;
 	_nc_mutex_init(&_nc_globals.mutex_curses);
+	_nc_mutex_init(&_nc_globals.mutex_prescreen);
+	_nc_mutex_init(&_nc_globals.mutex_screen);
+	_nc_mutex_init(&_nc_globals.mutex_update);
 	_nc_mutex_init(&_nc_globals.mutex_tst_tracef);
 	_nc_mutex_init(&_nc_globals.mutex_tracef);
     }
@@ -337,25 +362,28 @@
 NCURSES_EXPORT(int)
 _nc_mutex_lock(pthread_mutex_t * obj)
 {
-    if (_nc_use_pthreads == 0)
-	return 0;
-    return pthread_mutex_lock(obj);
+    int rc = 0;
+    if (_nc_use_pthreads != 0)
+	rc = pthread_mutex_lock(obj);
+    return rc;
 }
 
 NCURSES_EXPORT(int)
 _nc_mutex_trylock(pthread_mutex_t * obj)
 {
-    if (_nc_use_pthreads == 0)
-	return 0;
-    return pthread_mutex_trylock(obj);
+    int rc = 0;
+    if (_nc_use_pthreads != 0)
+	rc = pthread_mutex_trylock(obj);
+    return rc;
 }
 
 NCURSES_EXPORT(int)
 _nc_mutex_unlock(pthread_mutex_t * obj)
 {
-    if (_nc_use_pthreads == 0)
-	return 0;
-    return pthread_mutex_unlock(obj);
+    int rc = 0;
+    if (_nc_use_pthreads != 0)
+	rc = pthread_mutex_unlock(obj);
+    return rc;
 }
 #endif /* USE_PTHREADS */
 
@@ -371,7 +399,7 @@
     if ((pthread_sigmask))
 	return pthread_sigmask(how, newmask, oldmask);
     else
-	return sigprocmask(how, newmask, oldmask);
+	return (sigprocmask) (how, newmask, oldmask);
 }
 #endif
 #endif /* USE_PTHREADS */
diff --git a/ncurses/tinfo/lib_has_cap.c b/ncurses/tinfo/lib_has_cap.c
index 17e59d5..cbf68b3 100644
--- a/ncurses/tinfo/lib_has_cap.c
+++ b/ncurses/tinfo/lib_has_cap.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2009,2013 Free Software Foundation, Inc.              *
+ * Copyright 2020 Thomas E. Dickey                                          *
+ * Copyright 1998-2009,2013 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            *
@@ -46,7 +47,7 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_has_cap.c,v 1.10 2013/11/16 19:57:22 tom Exp $")
+MODULE_ID("$Id: lib_has_cap.c,v 1.11 2020/02/02 23:34:34 tom Exp $")
 
 NCURSES_EXPORT(bool)
 NCURSES_SP_NAME(has_ic) (NCURSES_SP_DCL0)
diff --git a/ncurses/tinfo/lib_kernel.c b/ncurses/tinfo/lib_kernel.c
index 37f7084..46f652b 100644
--- a/ncurses/tinfo/lib_kernel.c
+++ b/ncurses/tinfo/lib_kernel.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.              *
+ * Copyright 2020-2022,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2009,2010 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            *
@@ -48,8 +49,9 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_kernel.c,v 1.31 2010/12/19 01:21:19 tom Exp $")
+MODULE_ID("$Id: lib_kernel.c,v 1.36 2023/06/10 13:29:06 tom Exp $")
 
+#ifdef TERMIOS
 static int
 _nc_vdisable(void)
 {
@@ -57,7 +59,7 @@
 #if defined(_POSIX_VDISABLE) && HAVE_UNISTD_H
     value = _POSIX_VDISABLE;
 #endif
-#if defined(_PC_VDISABLE)
+#if defined(_PC_VDISABLE) && HAVE_FPATHCONF
     if (value == -1) {
 	value = (int) fpathconf(0, _PC_VDISABLE);
 	if (value == -1) {
@@ -70,6 +72,7 @@
 #endif
     return value;
 }
+#endif /* TERMIOS */
 
 /*
  *	erasechar()
@@ -91,6 +94,8 @@
 	result = termp->Ottyb.c_cc[VERASE];
 	if (result == _nc_vdisable())
 	    result = ERR;
+#elif defined(EXP_WIN32_DRIVER)
+	result = ERR;
 #else
 	result = termp->Ottyb.sg_erase;
 #endif
@@ -126,6 +131,8 @@
 	result = termp->Ottyb.c_cc[VKILL];
 	if (result == _nc_vdisable())
 	    result = ERR;
+#elif defined(EXP_WIN32_DRIVER)
+	result = ERR;
 #else
 	result = termp->Ottyb.sg_kill;
 #endif
@@ -141,30 +148,40 @@
 }
 #endif
 
+static void
+flush_input(int fd)
+{
+#ifdef TERMIOS
+    tcflush(fd, TCIFLUSH);
+#else /* !TERMIOS */
+    errno = 0;
+    do {
+#if defined(EXP_WIN32_DRIVER)
+	_nc_console_flush(_nc_console_fd2handle(fd));
+#else
+	ioctl(fd, TIOCFLUSH, 0);
+#endif
+    } while
+	(errno == EINTR);
+#endif
+}
+
 /*
  *	flushinp()
  *
- *	Flush any input on cur_term->Filedes
- *
+ *	Flush any input on tty
  */
 
 NCURSES_EXPORT(int)
 NCURSES_SP_NAME(flushinp) (NCURSES_SP_DCL0)
 {
-    TERMINAL *termp = TerminalOf(SP_PARM);
-
     T((T_CALLED("flushinp(%p)"), (void *) SP_PARM));
 
-    if (termp != 0) {
-#ifdef TERMIOS
-	tcflush(termp->Filedes, TCIFLUSH);
-#else
-	errno = 0;
-	do {
-	    ioctl(termp->Filedes, TIOCFLUSH, 0);
-	} while
-	    (errno == EINTR);
-#endif
+    if (SP_PARM != 0) {
+	if (NC_ISATTY(SP_PARM->_ifd))
+	    flush_input(SP_PARM->_ifd);
+	else if (NC_ISATTY(SP_PARM->_ofd))
+	    flush_input(SP_PARM->_ofd);
 	if (SP_PARM) {
 	    SP_PARM->_fifohead = -1;
 	    SP_PARM->_fifotail = 0;
diff --git a/ncurses/tinfo/lib_longname.c b/ncurses/tinfo/lib_longname.c
index fa231b8..9dc6d71 100644
--- a/ncurses/tinfo/lib_longname.c
+++ b/ncurses/tinfo/lib_longname.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2010,2015 Free Software Foundation, Inc.              *
+ * Copyright 2020,2021 Thomas E. Dickey                                     *
+ * Copyright 1998-2010,2015 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            *
@@ -42,7 +43,7 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_longname.c,v 1.13 2015/07/25 20:08:14 tom Exp $")
+MODULE_ID("$Id: lib_longname.c,v 1.15 2021/04/03 22:36:21 tom Exp $")
 
 #if USE_REENTRANT
 NCURSES_EXPORT(char *)
@@ -50,11 +51,12 @@
 {
     static char empty[] =
     {'\0'};
-    char *ptr;
 
     T((T_CALLED("longname(%p)"), (void *) SP_PARM));
 
     if (SP_PARM) {
+	char *ptr;
+
 	for (ptr = SP_PARM->_ttytype + strlen(SP_PARM->_ttytype);
 	     ptr > SP_PARM->_ttytype;
 	     ptr--)
diff --git a/ncurses/tinfo/lib_napms.c b/ncurses/tinfo/lib_napms.c
index df17363..1b89a54 100644
--- a/ncurses/tinfo/lib_napms.c
+++ b/ncurses/tinfo/lib_napms.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2014 Free Software Foundation, Inc.              *
+ * Copyright 2020,2023 Thomas E. Dickey                                     *
+ * Copyright 1998-2014,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            *
@@ -51,17 +52,18 @@
 #endif
 #endif
 
-MODULE_ID("$Id: lib_napms.c,v 1.24 2014/03/08 20:32:59 tom Exp $")
+MODULE_ID("$Id: lib_napms.c,v 1.28 2023/09/16 16:09:33 tom Exp $")
 
 NCURSES_EXPORT(int)
 NCURSES_SP_NAME(napms) (NCURSES_SP_DCLx int ms)
 {
     T((T_CALLED("napms(%d)"), ms));
 
+    if (ms > MAX_DELAY_MSECS)
+	ms = MAX_DELAY_MSECS;
+
 #ifdef USE_TERM_DRIVER
-    if (HasTerminal(SP_PARM)) {
-	CallDriver_1(SP_PARM, td_nap, ms);
-    }
+    CallDriver_1(SP_PARM, td_nap, ms);
 #else /* !USE_TERM_DRIVER */
 #if NCURSES_SP_FUNCS
     (void) sp;
@@ -76,6 +78,8 @@
 	    request = remaining;
 	}
     }
+#elif defined(_NC_WINDOWS)
+    Sleep((DWORD) ms);
 #else
     _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0));
 #endif
diff --git a/ncurses/tinfo/lib_options.c b/ncurses/tinfo/lib_options.c
index b736d5f..6a8bb39 100644
--- a/ncurses/tinfo/lib_options.c
+++ b/ncurses/tinfo/lib_options.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc.              *
+ * Copyright 2020-2021,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2014,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            *
@@ -46,7 +47,7 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_options.c,v 1.78 2014/09/27 21:55:24 tom Exp $")
+MODULE_ID("$Id: lib_options.c,v 1.83 2023/04/29 18:56:12 tom Exp $")
 
 NCURSES_EXPORT(int)
 idlok(WINDOW *win, bool flag)
@@ -87,11 +88,11 @@
 {
     T((T_CALLED("halfdelay(%p,%d)"), (void *) SP_PARM, t));
 
-    if (t < 1 || t > 255 || !IsValidTIScreen(SP_PARM))
+    if (t < 1 || t > 255 || !SP_PARM || !IsValidTIScreen(SP_PARM))
 	returnCode(ERR);
 
     NCURSES_SP_NAME(cbreak) (NCURSES_SP_ARG);
-    SP_PARM->_cbreak = t + 1;
+    IsCbreak(SP_PARM) = t + 1;
     returnCode(OK);
 }
 
@@ -202,7 +203,7 @@
 #ifdef USE_TERM_DRIVER
 	    code = CallDriver_1(SP_PARM, td_cursorSet, vis);
 #else
-	    if (IsTermInfo(SP_PARM)) {
+	    if (IsValidTIScreen(SP_PARM)) {
 		switch (vis) {
 		case 2:
 		    code = NCURSES_PUTP2_FLUSH("cursor_visible",
@@ -241,7 +242,7 @@
 NCURSES_SP_NAME(typeahead) (NCURSES_SP_DCLx int fd)
 {
     T((T_CALLED("typeahead(%p, %d)"), (void *) SP_PARM, fd));
-    if (IsValidTIScreen(SP_PARM)) {
+    if (SP_PARM && IsValidTIScreen(SP_PARM)) {
 	SP_PARM->_checkfd = fd;
 	returnCode(OK);
     } else {
@@ -360,7 +361,7 @@
 #else
 	    if (flag) {
 		(void) NCURSES_PUTP2_FLUSH("keypad_xmit", keypad_xmit);
-	    } else if (!flag && keypad_local) {
+	    } else if (keypad_local) {
 		(void) NCURSES_PUTP2_FLUSH("keypad_local", keypad_local);
 	    }
 
diff --git a/ncurses/tinfo/lib_print.c b/ncurses/tinfo/lib_print.c
index 0dab4d4..e7d8535 100644
--- a/ncurses/tinfo/lib_print.c
+++ b/ncurses/tinfo/lib_print.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2018-2021,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2011,2012 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            *
@@ -39,14 +40,14 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_print.c,v 1.23 2012/02/22 22:34:31 tom Exp $")
+MODULE_ID("$Id: lib_print.c,v 1.31 2023/06/10 12:44:41 tom Exp $")
 
 NCURSES_EXPORT(int)
 NCURSES_SP_NAME(mcprint) (NCURSES_SP_DCLx char *data, int len)
 /* ship binary character data to the printer via mc4/mc5/mc5p */
 {
     int result;
-    char *mybuf, *switchon;
+    char *mybuf = NULL, *switchon;
     size_t onsize, offsize;
     size_t need;
 
@@ -59,7 +60,7 @@
     }
 
     if (prtr_non) {
-	switchon = TPARM_1(prtr_non, len);
+	switchon = TIPARM_1(prtr_non, len);
 	onsize = strlen(switchon);
 	offsize = 0;
     } else {
@@ -72,6 +73,7 @@
 
     if (switchon == 0
 	|| (mybuf = typeMalloc(char, need + 1)) == 0) {
+	free(mybuf);
 	errno = ENOMEM;
 	return (ERR);
     }
@@ -88,14 +90,14 @@
      * data has actually been shipped to the terminal.  If the write(2)
      * operation is truly atomic we're protected from this.
      */
-    result = (int) write(TerminalOf(SP_PARM)->Filedes, mybuf, need);
+    result = (int) write(SP_PARM->_ofd, mybuf, need);
 
     /*
      * By giving up our scheduler slot here we increase the odds that the
      * kernel will ship the contiguous clist items from the last write
      * immediately.
      */
-#ifndef __MINGW32__
+#ifndef _NC_WINDOWS
     (void) sleep(0);
 #endif
     free(mybuf);
diff --git a/ncurses/tinfo/lib_raw.c b/ncurses/tinfo/lib_raw.c
index 928692b..fda6dea 100644
--- a/ncurses/tinfo/lib_raw.c
+++ b/ncurses/tinfo/lib_raw.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020-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            *
@@ -49,7 +50,7 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_raw.c,v 1.21 2012/01/21 19:21:29 KO.Myung-Hun Exp $")
+MODULE_ID("$Id: lib_raw.c,v 1.30 2024/03/30 15:54:17 tom Exp $")
 
 #if HAVE_SYS_TERMIO_H
 #include <sys/termio.h>		/* needed for ISC */
@@ -96,6 +97,8 @@
 	buf.c_iflag &= (unsigned) ~(COOKED_INPUT);
 	buf.c_cc[VMIN] = 1;
 	buf.c_cc[VTIME] = 0;
+#elif defined(EXP_WIN32_DRIVER)
+	buf.dwFlagIn &= (unsigned long) ~CONMODE_NORAW;
 #else
 	buf.sg_flags |= RAW;
 #endif
@@ -112,8 +115,10 @@
 	    kbdinfo.fsMask |= KEYBOARD_BINARY_MODE;
 	    KbdSetStatus(&kbdinfo, 0);
 #endif
-	    SP_PARM->_raw = TRUE;
-	    SP_PARM->_cbreak = 1;
+	    if (SP_PARM) {
+		IsRaw(SP_PARM) = TRUE;
+		IsCbreak(SP_PARM) = 1;
+	    }
 	    termp->Nttyb = buf;
 	}
 	AFTER("raw");
@@ -146,15 +151,19 @@
 #ifdef TERMIOS
 	buf.c_lflag &= (unsigned) ~ICANON;
 	buf.c_iflag &= (unsigned) ~ICRNL;
-	buf.c_lflag |= ISIG;
 	buf.c_cc[VMIN] = 1;
 	buf.c_cc[VTIME] = 0;
+#elif defined(EXP_WIN32_DRIVER)
+	buf.dwFlagIn |= CONMODE_NORAW;
+	buf.dwFlagIn &= (unsigned long) ~CONMODE_NOCBREAK;
 #else
 	buf.sg_flags |= CBREAK;
 #endif
 	result = NCURSES_SP_NAME(_nc_set_tty_mode) (NCURSES_SP_ARGx &buf);
 	if (result == OK) {
-	    SP_PARM->_cbreak = 1;
+	    if (SP_PARM) {
+		IsCbreak(SP_PARM) = 1;
+	    }
 	    termp->Nttyb = buf;
 	}
 	AFTER("cbreak");
@@ -177,12 +186,12 @@
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(qiflush) (NCURSES_SP_DCL0)
 {
-    int result = ERR;
     TERMINAL *termp;
 
     T((T_CALLED("qiflush(%p)"), (void *) SP_PARM));
     if ((termp = TerminalOf(SP_PARM)) != 0) {
 	TTY buf;
+	int result;
 
 	BEFORE("qiflush");
 	buf = termp->Nttyb;
@@ -190,6 +199,7 @@
 	buf.c_lflag &= (unsigned) ~(NOFLSH);
 	result = NCURSES_SP_NAME(_nc_set_tty_mode) (NCURSES_SP_ARGx &buf);
 #else
+	result = ERR;
 	/* FIXME */
 #endif
 	if (result == OK)
@@ -225,6 +235,8 @@
 	buf.c_lflag |= ISIG | ICANON |
 	    (termp->Ottyb.c_lflag & IEXTEN);
 	buf.c_iflag |= COOKED_INPUT;
+#elif defined(EXP_WIN32_DRIVER)
+	buf.dwFlagIn |= CONMODE_NORAW;
 #else
 	buf.sg_flags &= ~(RAW | CBREAK);
 #endif
@@ -241,8 +253,10 @@
 	    kbdinfo.fsMask |= KEYBOARD_ASCII_MODE;
 	    KbdSetStatus(&kbdinfo, 0);
 #endif
-	    SP_PARM->_raw = FALSE;
-	    SP_PARM->_cbreak = 0;
+	    if (SP_PARM) {
+		IsRaw(SP_PARM) = FALSE;
+		IsCbreak(SP_PARM) = 0;
+	    }
 	    termp->Nttyb = buf;
 	}
 	AFTER("noraw");
@@ -275,12 +289,16 @@
 #ifdef TERMIOS
 	buf.c_lflag |= ICANON;
 	buf.c_iflag |= ICRNL;
+#elif defined(EXP_WIN32_DRIVER)
+	buf.dwFlagIn |= (CONMODE_NOCBREAK | CONMODE_NORAW);
 #else
 	buf.sg_flags &= ~CBREAK;
 #endif
 	result = NCURSES_SP_NAME(_nc_set_tty_mode) (NCURSES_SP_ARGx &buf);
 	if (result == OK) {
-	    SP_PARM->_cbreak = 0;
+	    if (SP_PARM) {
+		IsCbreak(SP_PARM) = 0;
+	    }
 	    termp->Nttyb = buf;
 	}
 	AFTER("nocbreak");
@@ -299,12 +317,12 @@
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(noqiflush) (NCURSES_SP_DCL0)
 {
-    int result = ERR;
     TERMINAL *termp;
 
     T((T_CALLED("noqiflush(%p)"), (void *) SP_PARM));
     if ((termp = TerminalOf(SP_PARM)) != 0) {
 	TTY buf;
+	int result;
 
 	BEFORE("noqiflush");
 	buf = termp->Nttyb;
@@ -313,6 +331,7 @@
 	result = NCURSES_SP_NAME(_nc_set_tty_mode) (NCURSES_SP_ARGx &buf);
 #else
 	/* FIXME */
+	result = ERR;
 #endif
 	if (result == OK)
 	    termp->Nttyb = buf;
@@ -375,3 +394,46 @@
     return NCURSES_SP_NAME(intrflush) (CURRENT_SCREEN, win, flag);
 }
 #endif
+
+#if NCURSES_EXT_FUNCS
+/* *INDENT-OFF* */
+
+/*
+ * SCREEN is always opaque, but nl/raw/cbreak/echo set properties in it.
+ * As an extension, provide a way to query the properties.
+ *
+ * There are other properties which could be queried, e.g., filter, keypad,
+ * use_env, use_meta, but these particular properties are saved/restored within
+ * the wgetnstr() and wgetn_wstr() functions, which requires that the higher
+ * level curses library knows about the internal state of the lower level
+ * terminfo library.
+ */
+
+#define is_TEST(show,what) \
+    NCURSES_EXPORT(int) \
+    NCURSES_SP_NAME(show) (NCURSES_SP_DCL0) \
+    { \
+	return ((SP_PARM != NULL) ? (what(SP_PARM) ? 1 : 0) : -1); \
+    }
+
+is_TEST(is_nl, IsNl)
+is_TEST(is_raw, IsRaw)
+is_TEST(is_cbreak, IsCbreak)
+is_TEST(is_echo, IsEcho)
+
+#if NCURSES_SP_FUNCS
+#undef is_TEST
+#define is_TEST(show) \
+    NCURSES_EXPORT(int) \
+    show(void) \
+    { \
+	return NCURSES_SP_NAME(show) (CURRENT_SCREEN); \
+    }
+is_TEST(is_nl)
+is_TEST(is_raw)
+is_TEST(is_cbreak)
+is_TEST(is_echo)
+#endif
+
+/* *INDENT-ON* */
+#endif /* extensions */
diff --git a/ncurses/tinfo/lib_setup.c b/ncurses/tinfo/lib_setup.c
index 0a0a1f5..99097f0 100644
--- a/ncurses/tinfo/lib_setup.c
+++ b/ncurses/tinfo/lib_setup.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2014,2015 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            *
@@ -48,7 +49,7 @@
 #include <locale.h>
 #endif
 
-MODULE_ID("$Id: lib_setup.c,v 1.164 2015/06/27 18:10:55 tom Exp $")
+MODULE_ID("$Id: lib_setup.c,v 1.240 2024/04/20 17:04:05 tom Exp $")
 
 /****************************************************************************
  *
@@ -65,7 +66,7 @@
 #endif
 
 #if NEED_PTEM_H
- /* On SCO, they neglected to define struct winsize in termios.h -- it's only
+ /* On SCO, they neglected to define struct winsize in termios.h -- it is only
   * in termio.h and ptem.h (the former conflicts with other definitions).
   */
 # include <sys/stream.h>
@@ -98,7 +99,7 @@
  * Reduce explicit use of "cur_term" global variable.
  */
 #undef CUR
-#define CUR termp->type.
+#define CUR TerminalType(termp).
 
 /*
  * Wrap global variables in this module.
@@ -115,12 +116,12 @@
     if (CURRENT_SCREEN) {
 	TERMINAL *termp = TerminalOf(CURRENT_SCREEN);
 	if (termp != 0) {
-	    result = termp->type.term_names;
+	    result = TerminalType(termp).term_names;
 	}
     }
 #else
     if (cur_term != 0) {
-	result = cur_term->type.term_names;
+	result = TerminalType(cur_term).term_names;
     }
 #endif
     return result;
@@ -173,16 +174,20 @@
 NCURSES_SP_NAME(set_tabsize) (NCURSES_SP_DCLx int value)
 {
     int code = OK;
-#if USE_REENTRANT
-    if (SP_PARM) {
-	SP_PARM->_TABSIZE = value;
-    } else {
+    if (value <= 0) {
 	code = ERR;
-    }
+    } else {
+#if USE_REENTRANT
+	if (SP_PARM) {
+	    SP_PARM->_TABSIZE = value;
+	} else {
+	    code = ERR;
+	}
 #else
-    (void) SP_PARM;
-    TABSIZE = value;
+	(void) SP_PARM;
+	TABSIZE = value;
 #endif
+    }
     return code;
 }
 
@@ -220,9 +225,9 @@
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(use_env) (NCURSES_SP_DCLx bool f)
 {
+    START_TRACE();
     T((T_CALLED("use_env(%p,%d)"), (void *) SP_PARM, (int) f));
 #if NCURSES_SP_FUNCS
-    START_TRACE();
     if (IsPreScreen(SP_PARM)) {
 	SP_PARM->_use_env = f;
     }
@@ -235,11 +240,11 @@
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(use_tioctl) (NCURSES_SP_DCLx bool f)
 {
+    START_TRACE();
     T((T_CALLED("use_tioctl(%p,%d)"), (void *) SP_PARM, (int) f));
 #if NCURSES_SP_FUNCS
-    START_TRACE();
     if (IsPreScreen(SP_PARM)) {
-	SP_PARM->_use_tioctl = f;
+	SP_PARM->use_tioctl = f;
     }
 #else
     _nc_prescreen.use_tioctl = f;
@@ -251,8 +256,8 @@
 NCURSES_EXPORT(void)
 use_env(bool f)
 {
-    T((T_CALLED("use_env(%d)"), (int) f));
     START_TRACE();
+    T((T_CALLED("use_env(%d)"), (int) f));
     _nc_prescreen.use_env = f;
     returnVoid;
 }
@@ -260,17 +265,202 @@
 NCURSES_EXPORT(void)
 use_tioctl(bool f)
 {
-    T((T_CALLED("use_tioctl(%d)"), (int) f));
     START_TRACE();
+    T((T_CALLED("use_tioctl(%d)"), (int) f));
     _nc_prescreen.use_tioctl = f;
     returnVoid;
 }
 #endif
 
+#if !(defined(USE_TERM_DRIVER) || defined(EXP_WIN32_DRIVER))
+static void
+_nc_default_screensize(TERMINAL *termp, int *linep, int *colp)
+{
+    /* if we can't get dynamic info about the size, use static */
+    if (*linep <= 0) {
+	*linep = (int) lines;
+    }
+    if (*colp <= 0) {
+	*colp = (int) columns;
+    }
+
+    /* the ultimate fallback, assume fixed 24x80 size */
+    if (*linep <= 0) {
+	*linep = 24;
+    }
+    if (*colp <= 0) {
+	*colp = 80;
+    }
+}
+
+#if defined(USE_CHECK_SIZE) && defined(user6) && defined(user7)
+static const char *
+skip_csi(const char *value)
+{
+    if (UChar(*value) == CSI_CHR) {
+	++value;
+    } else if (*value == ESC_CHR && value[1] == L_BLOCK) {
+	value += 2;
+    }
+    return value;
+}
+
+static bool
+is_expected(const char *value, const char *expected)
+{
+    bool result = FALSE;
+    if (VALID_STRING(value)) {
+	const char *skipped = skip_csi(value);
+	if (skipped != value) {
+	    if (!strcmp(skipped, expected))
+		result = TRUE;
+	}
+    }
+    return result;
+}
+
+static bool
+get_position(TERMINAL *termp, int fd, int *row, int *col)
+{
+    bool result = FALSE;
+    size_t need = strlen(user7);
+    int have;
+
+    have = (int) write(fd, user7, need);
+
+    if (have == (int) need) {
+	int y, x;
+	char buf[20];
+	char *s;
+	char cc;
+	const char *skipped;
+	int scanned;
+
+	s = memset(buf, '\0', sizeof(buf));
+	do {
+	    size_t ask = (sizeof(buf) - 1 - (size_t) (s - buf));
+	    int got = (int) read(fd, s, ask);
+	    if (got == 0)
+		break;
+	    s += got;
+	    *s = '\0';
+	} while (strchr(buf, 'R') == NULL && (size_t) (s + 1 - buf) < sizeof(buf));
+	T(("CPR response %s", _nc_visbuf(buf)));
+	skipped = skip_csi(buf);
+	cc = '\0';
+	if (skipped != buf
+	    && *skipped != '\0'
+	    && (scanned = sscanf(skip_csi(buf), "%d;%d%c", &y, &x, &cc)) == 3
+	    && (cc == 'R')) {
+	    *row = y;
+	    *col = x;
+	    result = TRUE;
+	}
+    }
+    T(("get_position %s %d,%d", result ? "OK" : "ERR", *row, *col));
+    return result;
+}
+
+static bool
+set_position(NCURSES_SP_DCLx TERMINAL *termp, int row, int col)
+{
+    bool result;
+    char *actual = TIPARM_2(cursor_address, row, col);
+    T((T_CALLED("set_position %d,%d)"), row, col));
+#if NCURSES_SP_FUNCS
+    result = (NCURSES_SP_NAME(_nc_putp) (NCURSES_SP_ARGx "set_position",
+					 actual) == OK);
+    NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_ARG);
+#else
+    /* This does not support padding because without sp-funcs, we have only
+     * the interface using stdio, but we are not guaranteed that Filedes
+     * is the same as fileno(stdout).
+     */
+    result = FALSE;
+    if (actual != NULL) {
+	size_t want = strlen(actual);
+	int have = (int) write(termp->Filedes, actual, want);
+	result = ((int) want == have);
+    }
+#endif
+    returnBool(result);
+}
+
+/*
+ * This is a little more complicated than one might expect, because we do this
+ * before setting up the terminal modes, etc., and cannot use the timeout or
+ * buffering functions.
+ *
+ * We check if the terminal description has the ECMA-48 CPR (cursor position
+ * report) in u7 and the response in u6.  The two variations of is_expected()
+ * cover the termcap style and terminfo style, and are equivalent as far as we
+ * are concerned.  For analyzing the response, we wait (a short time) for 'R'
+ * to be echoed, and then check if we received two integers in the response.
+ *
+ * In principle, this could run on "any" ECMA-48 terminal, but in practice,
+ * there is a scenario using GNU screen where it uses ncurses with a partially
+ * configured pseudo-terminal, and the CPR response goes to the wrong place.
+ * So we do a simple check to exclude pseudo-terminals.
+ */
+static void
+_nc_check_screensize(SCREEN *sp, TERMINAL *termp, int *linep, int *colp)
+{
+    int fd = termp->Filedes;
+    TTY saved;
+    const char *name = NULL;
+
+    if (IsRealTty(fd, name)
+	&& VALID_STRING(cursor_address)
+	&& is_expected(user7, "6n")
+	&& (is_expected(user6, "%i%d;%dR") ||
+	    is_expected(user6, "%i%p1%d;%p2%dR"))
+	&& GET_TTY(fd, &saved) == OK) {
+	int current_y = -1, current_x = -1;
+	int updated_y = -1, updated_x = -1;
+	TTY alter = saved;
+
+#if NCURSES_SP_FUNCS
+	if (sp == NULL) {
+	    sp = new_prescr();
+	    sp->_term = termp;
+	    NCURSES_SP_NAME(baudrate) (NCURSES_SP_ARG);
+	}
+#else
+	(void) sp;
+#endif
+
+	T(("trying CPR (u7/u6) with %s", name));
+	alter.c_lflag &= (unsigned) ~(ECHO | ICANON | ISIG | IEXTEN);
+	alter.c_iflag &= (unsigned) ~(IXON | BRKINT | PARMRK);
+	alter.c_cc[VMIN] = 0;
+	alter.c_cc[VTIME] = 1;
+	SET_TTY(fd, &alter);
+
+	if (get_position(termp, fd, &current_y, &current_x)
+	    && set_position(NCURSES_SP_ARGx termp, 9999, 9999)
+	    && get_position(termp, fd, &updated_y, &updated_x)) {
+	    *linep = updated_y;
+	    *colp = updated_x;
+	    set_position(NCURSES_SP_ARGx termp, current_y, current_x);
+	}
+	/* restore tty modes */
+	SET_TTY(fd, &saved);
+    } else {
+	T(("NOT trying CPR with fd %d (%s): %s",
+	   fd, NonNull(name), NC_ISATTY(fd) ? "tty" : "not a tty"));
+    }
+
+    _nc_default_screensize(termp, linep, colp);
+}
+#else /* !USE_CHECK_SIZE */
+#define _nc_check_screensize(sp, termp, linep, colp)	/* nothing */
+#endif
+#endif /* !(defined(USE_TERM_DRIVER) || defined(EXP_WIN32_DRIVER)) */
+
 NCURSES_EXPORT(void)
 _nc_get_screensize(SCREEN *sp,
 #ifdef USE_TERM_DRIVER
-		   TERMINAL * termp,
+		   TERMINAL *termp,
 #endif
 		   int *linep, int *colp)
 /* Obtain lines/columns values from the environment and/or terminfo entry */
@@ -297,16 +487,33 @@
 #else /* !USE_TERM_DRIVER */
     TERMINAL *termp = cur_term;
     int my_tabsize;
+    bool useEnv = _nc_prescreen.use_env;
+    bool useTioctl = _nc_prescreen.use_tioctl;
 
+    T((T_CALLED("_nc_get_screensize (%p)"), (void *) sp));
+#ifdef EXP_WIN32_DRIVER
+    /* If we are here, then Windows console is used in terminfo mode.
+       We need to figure out the size using the console API
+     */
+    _nc_console_size(linep, colp);
+    T(("screen size: winconsole lines = %d columns = %d", *linep, *colp));
+#else
     /* figure out the size of the screen */
     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
 
     *linep = (int) lines;
     *colp = (int) columns;
+#endif
 
-    if (_nc_prescreen.use_env || _nc_prescreen.use_tioctl) {
-	int value;
+#if NCURSES_SP_FUNCS
+    if (sp) {
+	useEnv = sp->_use_env;
+	useTioctl = sp->use_tioctl;
+    }
+#endif
 
+    T(("useEnv:%d useTioctl:%d", useEnv, useTioctl));
+    if (useEnv || useTioctl) {
 #ifdef __EMX__
 	{
 	    int screendata[2];
@@ -340,8 +547,10 @@
 	}
 #endif /* HAVE_SIZECHANGE */
 
-	if (_nc_prescreen.use_env) {
-	    if (_nc_prescreen.use_tioctl) {
+	if (useEnv) {
+	    int value;
+
+	    if (useTioctl) {
 		/*
 		 * If environment variables are used, update them.
 		 */
@@ -367,30 +576,26 @@
 		*colp = value;
 		T(("screen size: environment COLUMNS = %d", *colp));
 	    }
-	}
 
-	/* if we can't get dynamic info about the size, use static */
-	if (*linep <= 0) {
-	    *linep = (int) lines;
-	}
-	if (*colp <= 0) {
-	    *colp = (int) columns;
-	}
-
-	/* the ultimate fallback, assume fixed 24x80 size */
-	if (*linep <= 0) {
-	    *linep = 24;
-	}
-	if (*colp <= 0) {
-	    *colp = 80;
+	    _nc_default_screensize(termp, linep, colp);
+	} else {
+	    _nc_check_screensize(sp, termp, linep, colp);
 	}
 
 	/*
 	 * Put the derived values back in the screen-size caps, so
 	 * tigetnum() and tgetnum() will do the right thing.
 	 */
-	lines = (short) (*linep);
-	columns = (short) (*colp);
+	lines = (NCURSES_INT2) (*linep);
+	columns = (NCURSES_INT2) (*colp);
+#if NCURSES_EXT_NUMBERS
+#define OldNumber(termp,name) \
+	(termp)->type.Numbers[(&name - (termp)->type2.Numbers)]
+	OldNumber(termp, lines) = (short) (*linep);
+	OldNumber(termp, columns) = (short) (*colp);
+#endif
+    } else {
+	_nc_check_screensize(sp, termp, linep, colp);
     }
 
     T(("screen size is %dx%d", *linep, *colp));
@@ -407,6 +612,7 @@
     TABSIZE = my_tabsize;
 #endif
     T(("TABSIZE = %d", TABSIZE));
+    returnVoid;
 #endif /* USE_TERM_DRIVER */
 }
 
@@ -431,23 +637,24 @@
     int old_cols = columns;
 #endif
 
-    TINFO_GET_SIZE(sp, sp->_term, &new_lines, &new_cols);
-
-    /*
-     * See is_term_resized() and resizeterm().
-     * We're doing it this way because those functions belong to the upper
-     * ncurses library, while this resides in the lower terminfo library.
-     */
-    if (sp != 0 && sp->_resize != 0) {
-	if ((new_lines != old_lines) || (new_cols != old_cols)) {
-	    sp->_resize(NCURSES_SP_ARGx new_lines, new_cols);
-	} else if (sp->_sig_winch && (sp->_ungetch != 0)) {
-	    sp->_ungetch(SP_PARM, KEY_RESIZE);	/* so application can know this */
+    if (sp != 0) {
+	TINFO_GET_SIZE(sp, sp->_term, &new_lines, &new_cols);
+	/*
+	 * See is_term_resized() and resizeterm().
+	 * We're doing it this way because those functions belong to the upper
+	 * ncurses library, while this resides in the lower terminfo library.
+	 */
+	if (sp->_resize != 0) {
+	    if ((new_lines != old_lines) || (new_cols != old_cols)) {
+		sp->_resize(NCURSES_SP_ARGx new_lines, new_cols);
+	    } else if (sp->_sig_winch && (sp->_ungetch != 0)) {
+		sp->_ungetch(SP_PARM, KEY_RESIZE);	/* so application can know this */
+	    }
+	    sp->_sig_winch = FALSE;
 	}
-	sp->_sig_winch = FALSE;
     }
 }
-#endif
+#endif /* USE_SIZECHANGE */
 
 /****************************************************************************
  *
@@ -461,10 +668,10 @@
  * just like tgetent().
  */
 int
-_nc_setup_tinfo(const char *const tn, TERMTYPE *const tp)
+_nc_setup_tinfo(const char *const tn, TERMTYPE2 *const tp)
 {
     char filename[PATH_MAX];
-    int status = _nc_read_entry(tn, filename, tp);
+    int status = _nc_read_entry2(tn, filename, tp);
 
     /*
      * If we have an entry, force all of the cancelled strings to null
@@ -474,6 +681,7 @@
      */
     if (status == TGETENT_YES) {
 	unsigned n;
+	T(("_nc_setup_tinfo - resetting invalid booleans/strings"));
 	for_each_boolean(n, tp) {
 	    if (!VALID_BOOLEAN(tp->Booleans[n]))
 		tp->Booleans[n] = FALSE;
@@ -488,14 +696,12 @@
 #endif
 
 /*
-**	Take the real command character out of the CC environment variable
-**	and substitute it in for the prototype given in 'command_character'.
-*/
+ * Take the real command character out of the CC environment variable
+ * and substitute it in for the prototype given in 'command_character'.
+ */
 void
-_nc_tinfo_cmdch(TERMINAL * termp, int proto)
+_nc_tinfo_cmdch(TERMINAL *termp, int proto)
 {
-    unsigned i;
-    char CC;
     char *tmp;
 
     /*
@@ -504,11 +710,16 @@
      * name as an environment variable - using the same symbol.
      */
     if ((tmp = getenv("CC")) != 0 && strlen(tmp) == 1) {
-	CC = *tmp;
+	unsigned i;
+	char CC = *tmp;
+
 	for_each_string(i, &(termp->type)) {
-	    for (tmp = termp->type.Strings[i]; tmp && *tmp; tmp++) {
-		if (UChar(*tmp) == proto)
-		    *tmp = CC;
+	    tmp = termp->type.Strings[i];
+	    if (VALID_STRING(tmp)) {
+		for (; *tmp; ++tmp) {
+		    if (UChar(*tmp) == proto)
+			*tmp = CC;
+		}
 	    }
 	}
     }
@@ -528,9 +739,9 @@
      */
     env = setlocale(LC_CTYPE, 0);
 #else
-    if (((env = getenv("LC_ALL")) != 0 && *env != '\0')
+    if (((env = getenv("LANG")) != 0 && *env != '\0')
 	|| ((env = getenv("LC_CTYPE")) != 0 && *env != '\0')
-	|| ((env = getenv("LANG")) != 0 && *env != '\0')) {
+	|| ((env = getenv("LC_ALL")) != 0 && *env != '\0')) {
 	;
     }
 #endif
@@ -544,22 +755,27 @@
 NCURSES_EXPORT(int)
 _nc_unicode_locale(void)
 {
-    int result = 0;
-#if defined(__MINGW32__) && USE_WIDEC_SUPPORT
-    result = 1;
+    static bool initialized = FALSE;
+    static int result = 0;
+
+    if (!initialized) {
+#if defined(_NC_WINDOWS) && USE_WIDEC_SUPPORT
+	result = 1;
 #elif HAVE_LANGINFO_CODESET
-    char *env = nl_langinfo(CODESET);
-    result = !strcmp(env, "UTF-8");
-    T(("_nc_unicode_locale(%s) ->%d", env, result));
+	char *env = nl_langinfo(CODESET);
+	result = !strcmp(env, "UTF-8");
+	T(("_nc_unicode_locale(%s) ->%d", env, result));
 #else
-    char *env = _nc_get_locale();
-    if (env != 0) {
-	if (strstr(env, ".UTF-8") != 0) {
-	    result = 1;
-	    T(("_nc_unicode_locale(%s) ->%d", env, result));
+	char *env = _nc_get_locale();
+	if (env != 0) {
+	    if (strstr(env, ".UTF-8") != 0) {
+		result = 1;
+		T(("_nc_unicode_locale(%s) ->%d", env, result));
+	    }
 	}
-    }
 #endif
+	initialized = TRUE;
+    }
     return result;
 }
 
@@ -571,7 +787,7 @@
  * character set.
  */
 NCURSES_EXPORT(int)
-_nc_locale_breaks_acs(TERMINAL * termp)
+_nc_locale_breaks_acs(TERMINAL *termp)
 {
     const char *env_name = "NCURSES_NO_UTF8_ACS";
     const char *env;
@@ -602,19 +818,18 @@
 }
 
 NCURSES_EXPORT(int)
-TINFO_SETUP_TERM(TERMINAL ** tp,
-		 NCURSES_CONST char *tname,
+TINFO_SETUP_TERM(TERMINAL **tp,
+		 const char *tname,
 		 int Filedes,
 		 int *errret,
 		 int reuse)
 {
 #ifdef USE_TERM_DRIVER
     TERMINAL_CONTROL_BLOCK *TCB = 0;
-#else
-    int status;
 #endif
     TERMINAL *termp;
     SCREEN *sp = 0;
+    char *myname;
     int code = ERR;
 
     START_TRACE();
@@ -635,22 +850,30 @@
 
     if (tname == 0) {
 	tname = getenv("TERM");
-	if (tname == 0 || *tname == '\0') {
-#ifdef USE_TERM_DRIVER
+#if defined(EXP_WIN32_DRIVER)
+	if (!VALID_TERM_ENV(tname, NO_TERMINAL)) {
+	    T(("Failure with TERM=%s", NonNull(tname)));
+	    ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
+	}
+#elif defined(USE_TERM_DRIVER)
+	if (!NonEmpty(tname))
 	    tname = "unknown";
 #else
+	if (!NonEmpty(tname)) {
+	    T(("Failure with TERM=%s", NonNull(tname)));
 	    ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
-#endif
 	}
+#endif
     }
-
-    if (strlen(tname) > MAX_NAME_SIZE) {
+    myname = strdup(tname);
+    if (myname == NULL || strlen(myname) > MAX_NAME_SIZE) {
 	ret_error(TGETENT_ERR,
-		  "TERM environment must be <= %d characters.\n",
-		  MAX_NAME_SIZE);
+		  "TERM environment must be 1..%d characters.\n",
+		  MAX_NAME_SIZE,
+		  free(myname));
     }
 
-    T(("your terminal name is %s", tname));
+    T(("your terminal name is %s", myname));
 
     /*
      * Allow output redirection.  This is what SVr3 does.  If stdout is
@@ -658,6 +881,10 @@
      */
     if (Filedes == STDOUT_FILENO && !NC_ISATTY(Filedes))
 	Filedes = STDERR_FILENO;
+#if defined(EXP_WIN32_DRIVER)
+    if (Filedes != STDERR_FILENO && NC_ISATTY(Filedes))
+	_setmode(Filedes, _O_BINARY);
+#endif
 
     /*
      * Check if we have already initialized to use this terminal.  If so, we
@@ -679,8 +906,8 @@
 	&& (termp != 0)
 	&& termp->Filedes == Filedes
 	&& termp->_termname != 0
-	&& !strcmp(termp->_termname, tname)
-	&& _nc_name_match(termp->type.term_names, tname, "|")) {
+	&& !strcmp(termp->_termname, myname)
+	&& _nc_name_match(TerminalType(termp).term_names, myname, "|")) {
 	T(("reusing existing terminal information and mode-settings"));
 	code = OK;
 #ifdef USE_TERM_DRIVER
@@ -689,39 +916,70 @@
     } else {
 #ifdef USE_TERM_DRIVER
 	TERMINAL_CONTROL_BLOCK *my_tcb;
-	my_tcb = typeCalloc(TERMINAL_CONTROL_BLOCK, 1);
-	termp = &(my_tcb->term);
+	termp = 0;
+	if ((my_tcb = typeCalloc(TERMINAL_CONTROL_BLOCK, 1)) != 0)
+	    termp = &(my_tcb->term);
 #else
+	int status;
+
 	termp = typeCalloc(TERMINAL, 1);
 #endif
 	if (termp == 0) {
-	    ret_error0(TGETENT_ERR,
-		       "Not enough memory to create terminal structure.\n");
+	    ret_error1(TGETENT_ERR,
+		       "Not enough memory to create terminal structure.\n",
+		       myname, free(myname));
 	}
+	++_nc_globals.terminal_count;
+#if HAVE_SYSCONF
+	{
+	    long limit;
+#ifdef LINE_MAX
+	    limit = LINE_MAX;
+#else
+	    limit = _nc_globals.getstr_limit;
+#endif
+#ifdef _SC_LINE_MAX
+	    if (limit < sysconf(_SC_LINE_MAX))
+		limit = sysconf(_SC_LINE_MAX);
+#endif
+	    if (_nc_globals.getstr_limit < (int) limit)
+		_nc_globals.getstr_limit = (int) limit;
+	}
+#endif /* HAVE_SYSCONF */
+	T(("using %d for getstr limit", _nc_globals.getstr_limit));
+
 #ifdef USE_TERM_DRIVER
 	INIT_TERM_DRIVER();
+	/*
+	 * _nc_get_driver() will call td_CanHandle() for each driver, and win_driver
+	 * needs file descriptor to do the test, so set it before calling.
+	 */
+	termp->Filedes = (short) Filedes;
 	TCB = (TERMINAL_CONTROL_BLOCK *) termp;
-	code = _nc_globals.term_driver(TCB, tname, errret);
+	code = _nc_globals.term_driver(TCB, myname, errret);
 	if (code == OK) {
-	    termp->Filedes = (short) Filedes;
-	    termp->_termname = strdup(tname);
+	    termp->_termname = strdup(myname);
 	} else {
-	    ret_error0(errret ? *errret : TGETENT_ERR,
-		       "Could not find any driver to handle this terminal.\n");
+	    ret_error1(errret ? *errret : TGETENT_ERR,
+		       "Could not find any driver to handle terminal.\n",
+		       myname, free(myname));
 	}
 #else
 #if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
-	status = _nc_setup_tinfo(tname, &termp->type);
+	status = _nc_setup_tinfo(myname, &TerminalType(termp));
+	T(("_nc_setup_tinfo returns %d", status));
 #else
+	T(("no database available"));
 	status = TGETENT_NO;
 #endif
 
 	/* try fallback list if entry on disk */
 	if (status != TGETENT_YES) {
-	    const TERMTYPE *fallback = _nc_fallback(tname);
+	    const TERMTYPE2 *fallback = _nc_fallback2(myname);
 
 	    if (fallback) {
-		_nc_copy_termtype(&(termp->type), fallback);
+		T(("found fallback entry"));
+		_nc_copy_termtype2(&(TerminalType(termp)), fallback);
 		status = TGETENT_YES;
 	    }
 	}
@@ -729,33 +987,41 @@
 	if (status != TGETENT_YES) {
 	    del_curterm(termp);
 	    if (status == TGETENT_ERR) {
+		free(myname);
 		ret_error0(status, "terminals database is inaccessible\n");
 	    } else if (status == TGETENT_NO) {
-		ret_error1(status, "unknown terminal type.\n", tname);
+		ret_error1(status, "unknown terminal type.\n",
+			   myname, free(myname));
+	    } else {
+		free(myname);
+		ret_error0(status, "unexpected return-code\n");
 	    }
 	}
+#if NCURSES_EXT_NUMBERS
+	_nc_export_termtype2(&termp->type, &TerminalType(termp));
+#endif
 #if !USE_REENTRANT
-	strncpy(ttytype, termp->type.term_names, (size_t) (NAMESIZE - 1));
-	ttytype[NAMESIZE - 1] = '\0';
+	save_ttytype(termp);
 #endif
 
 	termp->Filedes = (short) Filedes;
-	termp->_termname = strdup(tname);
+	termp->_termname = strdup(myname);
 
 	set_curterm(termp);
 
-	if (command_character)
+	if (VALID_STRING(command_character))
 	    _nc_tinfo_cmdch(termp, UChar(*command_character));
 
 	/*
 	 * If an application calls setupterm() rather than initscr() or
 	 * newterm(), we will not have the def_prog_mode() call in
 	 * _nc_setupscreen().  Do it now anyway, so we can initialize the
-	 * baudrate.
+	 * baudrate.  Also get the shell-mode so that erasechar() works.
 	 */
 	if (NC_ISATTY(Filedes)) {
-	    def_prog_mode();
-	    baudrate();
+	    NCURSES_SP_NAME(def_shell_mode) (NCURSES_SP_ARG);
+	    NCURSES_SP_NAME(def_prog_mode) (NCURSES_SP_ARG);
+	    NCURSES_SP_NAME(baudrate) (NCURSES_SP_ARG);
 	}
 	code = OK;
 #endif
@@ -786,36 +1052,99 @@
 	if ((VALID_STRING(cursor_address)
 	     || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home)))
 	    && VALID_STRING(clear_screen)) {
-	    ret_error1(TGETENT_YES, "terminal is not really generic.\n", tname);
+	    ret_error1(TGETENT_YES, "terminal is not really generic.\n",
+		       myname, free(myname));
 	} else {
 	    del_curterm(termp);
-	    ret_error1(TGETENT_NO, "I need something more specific.\n", tname);
+	    ret_error1(TGETENT_NO, "I need something more specific.\n",
+		       myname, free(myname));
 	}
     } else if (hard_copy) {
-	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n", tname);
+	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n",
+		   myname, free(myname));
     }
 #endif
+    free(myname);
     returnCode(code);
 }
 
+#ifdef USE_PTHREADS
+/*
+ * Returns a non-null pointer unless a new screen should be allocated because
+ * no match was found in the pre-screen cache.
+ */
+NCURSES_EXPORT(SCREEN *)
+_nc_find_prescr(void)
+{
+    SCREEN *result = 0;
+    PRESCREEN_LIST *p;
+    pthread_t id = GetThreadID();
+    for (p = _nc_prescreen.allocated; p != 0; p = p->next) {
+	if (p->id == id) {
+	    result = p->sp;
+	    break;
+	}
+    }
+    return result;
+}
+
+/*
+ * Tells ncurses to forget that this thread was associated with the pre-screen
+ * cache.  It does not modify the pre-screen cache itself, since that is used
+ * for creating new screens.
+ */
+NCURSES_EXPORT(void)
+_nc_forget_prescr(void)
+{
+    PRESCREEN_LIST *p, *q;
+    pthread_t id = GetThreadID();
+    _nc_lock_global(screen);
+    for (p = _nc_prescreen.allocated, q = 0; p != 0; q = p, p = p->next) {
+	if (p->id == id) {
+	    if (q) {
+		q->next = p->next;
+	    } else {
+		_nc_prescreen.allocated = p->next;
+	    }
+	    free(p);
+	    break;
+	}
+    }
+    _nc_unlock_global(screen);
+}
+#endif /* USE_PTHREADS */
+
 #if NCURSES_SP_FUNCS
 /*
  * In case of handling multiple screens, we need to have a screen before
- * initialization in setupscreen takes place.  This is to extend the substitute
- * for some of the stuff in _nc_prescreen, especially for slk and ripoff
- * handling which should be done per screen.
+ * initialization in _nc_setupscreen takes place.  This is to extend the
+ * substitute for some of the stuff in _nc_prescreen, especially for slk and
+ * ripoff handling which should be done per screen.
  */
 NCURSES_EXPORT(SCREEN *)
 new_prescr(void)
 {
-    static SCREEN *sp;
+    SCREEN *sp;
 
     START_TRACE();
     T((T_CALLED("new_prescr()")));
 
-    if (sp == 0) {
+    _nc_lock_global(screen);
+    if ((sp = _nc_find_prescr()) == 0) {
 	sp = _nc_alloc_screen_sp();
+	T(("_nc_alloc_screen_sp %p", (void *) sp));
 	if (sp != 0) {
+#ifdef USE_PTHREADS
+	    PRESCREEN_LIST *p = typeCalloc(PRESCREEN_LIST, 1);
+	    if (p != 0) {
+		p->id = GetThreadID();
+		p->sp = sp;
+		p->next = _nc_prescreen.allocated;
+		_nc_prescreen.allocated = p;
+	    }
+#else
+	    _nc_prescreen.allocated = sp;
+#endif
 	    sp->rsp = sp->rippedoff;
 	    sp->_filtered = _nc_prescreen.filter_mode;
 	    sp->_use_env = _nc_prescreen.use_env;
@@ -831,7 +1160,10 @@
 	    sp->_ESCDELAY = _nc_prescreen._ESCDELAY;
 #endif
 	}
+    } else {
+	T(("_nc_alloc_screen_sp %p (reuse)", (void *) sp));
     }
+    _nc_unlock_global(screen);
     returnSP(sp);
 }
 #endif
@@ -842,17 +1174,26 @@
  * the same TERMINAL data (see comment).
  */
 NCURSES_EXPORT(int)
-_nc_setupterm(NCURSES_CONST char *tname,
+_nc_setupterm(const char *tname,
 	      int Filedes,
 	      int *errret,
 	      int reuse)
 {
-    int res;
+    int rc = ERR;
     TERMINAL *termp = 0;
-    res = TINFO_SETUP_TERM(&termp, tname, Filedes, errret, reuse);
-    if (ERR != res)
-	NCURSES_SP_NAME(set_curterm) (CURRENT_SCREEN_PRE, termp);
-    return res;
+
+    _nc_init_pthreads();
+    _nc_lock_global(prescreen);
+    START_TRACE();
+    if (TINFO_SETUP_TERM(&termp, tname, Filedes, errret, reuse) == OK) {
+	_nc_forget_prescr();
+	if (NCURSES_SP_NAME(set_curterm) (CURRENT_SCREEN_PRE, termp) != 0) {
+	    rc = OK;
+	}
+    }
+    _nc_unlock_global(prescreen);
+
+    return rc;
 }
 #endif
 
@@ -863,7 +1204,7 @@
  *	Make cur_term point to the structure.
  */
 NCURSES_EXPORT(int)
-setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
+setupterm(const char *tname, int Filedes, int *errret)
 {
     START_TRACE();
     return _nc_setupterm(tname, Filedes, errret, FALSE);
diff --git a/ncurses/tinfo/lib_termcap.c b/ncurses/tinfo/lib_termcap.c
index fdfc494..2ece985 100644
--- a/ncurses/tinfo/lib_termcap.c
+++ b/ncurses/tinfo/lib_termcap.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2020,2023 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            *
@@ -48,7 +49,7 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_termcap.c,v 1.80 2013/06/08 16:48:47 tom Exp $")
+MODULE_ID("$Id: lib_termcap.c,v 1.89 2023/05/27 20:13:10 tom Exp $")
 
 NCURSES_EXPORT_VAR(char *) UP = 0;
 NCURSES_EXPORT_VAR(char *) BC = 0;
@@ -100,8 +101,7 @@
     START_TRACE();
     T((T_CALLED("tgetent()")));
 
-    TINFO_SETUP_TERM(&termp, (NCURSES_CONST char *) name,
-		     STDOUT_FILENO, &rc, TRUE);
+    TINFO_SETUP_TERM(&termp, name, STDOUT_FILENO, &rc, TRUE);
 
 #ifdef USE_TERM_DRIVER
     if (termp == 0 ||
@@ -153,8 +153,12 @@
 	}
 	CacheInx = best;
     }
-    LAST_TRM = TerminalOf(SP_PARM);
-    LAST_SEQ = ++CacheSeq;
+    if (rc == 1) {
+	LAST_TRM = TerminalOf(SP_PARM);
+	LAST_SEQ = ++CacheSeq;
+    } else {
+	LAST_TRM = 0;
+    }
 
     PC = 0;
     UP = 0;
@@ -175,7 +179,8 @@
 	if (backspace_if_not_bs != NULL)
 	    BC = backspace_if_not_bs;
 
-	if ((FIX_SGR0 = _nc_trim_sgr0(&(TerminalOf(SP_PARM)->type))) != 0) {
+	if ((FIX_SGR0 = _nc_trim_sgr0(&TerminalType(TerminalOf(SP_PARM))))
+	    != 0) {
 	    if (!strcmp(FIX_SGR0, exit_attribute_mode)) {
 		if (FIX_SGR0 != exit_attribute_mode) {
 		    free(FIX_SGR0);
@@ -230,15 +235,15 @@
  ***************************************************************************/
 
 NCURSES_EXPORT(int)
-NCURSES_SP_NAME(tgetflag) (NCURSES_SP_DCLx NCURSES_CONST char *id)
+NCURSES_SP_NAME(tgetflag) (NCURSES_SP_DCLx const char *id)
 {
     int result = 0;		/* Solaris returns zero for missing flag */
-    int j = -1;
 
     T((T_CALLED("tgetflag(%p, %s)"), (void *) SP_PARM, id));
     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(id, BOOLEAN, TRUE);
 	if (entry_ptr != 0) {
@@ -266,7 +271,7 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(int)
-tgetflag(NCURSES_CONST char *id)
+tgetflag(const char *id)
 {
     return NCURSES_SP_NAME(tgetflag) (CURRENT_SCREEN, id);
 }
@@ -282,15 +287,15 @@
  ***************************************************************************/
 
 NCURSES_EXPORT(int)
-NCURSES_SP_NAME(tgetnum) (NCURSES_SP_DCLx NCURSES_CONST char *id)
+NCURSES_SP_NAME(tgetnum) (NCURSES_SP_DCLx const char *id)
 {
     int result = ABSENT_NUMERIC;
-    int j = -1;
 
     T((T_CALLED("tgetnum(%p, %s)"), (void *) SP_PARM, id));
     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(id, NUMBER, TRUE);
 	if (entry_ptr != 0) {
@@ -318,7 +323,7 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(int)
-tgetnum(NCURSES_CONST char *id)
+tgetnum(const char *id)
 {
     return NCURSES_SP_NAME(tgetnum) (CURRENT_SCREEN, id);
 }
@@ -334,15 +339,15 @@
  ***************************************************************************/
 
 NCURSES_EXPORT(char *)
-NCURSES_SP_NAME(tgetstr) (NCURSES_SP_DCLx NCURSES_CONST char *id, char **area)
+NCURSES_SP_NAME(tgetstr) (NCURSES_SP_DCLx const char *id, char **area)
 {
     char *result = NULL;
-    int j = -1;
 
     T((T_CALLED("tgetstr(%s,%p)"), id, (void *) area));
     if (HasTInfoTerminal(SP_PARM) && ValidCap(id)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(id, STRING, TRUE);
 	if (entry_ptr != 0) {
@@ -384,20 +389,41 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(char *)
-tgetstr(NCURSES_CONST char *id, char **area)
+tgetstr(const char *id, char **area)
 {
     return NCURSES_SP_NAME(tgetstr) (CURRENT_SCREEN, id, area);
 }
 #endif
 
 #if NO_LEAKS
+#undef CacheInx
+#define CacheInx num
+NCURSES_EXPORT(void)
+_nc_tgetent_leak(const TERMINAL *const termp)
+{
+    if (termp != 0) {
+	int num;
+	for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) {
+	    if (LAST_TRM == termp) {
+		FreeAndNull(FIX_SGR0);
+		if (LAST_TRM != 0) {
+		    LAST_TRM = 0;
+		}
+		break;
+	    }
+	}
+    }
+}
+
 NCURSES_EXPORT(void)
 _nc_tgetent_leaks(void)
 {
+    int num;
     for (CacheInx = 0; CacheInx < TGETENT_MAX; ++CacheInx) {
-	FreeIfNeeded(FIX_SGR0);
-	if (LAST_TRM != 0)
+	if (LAST_TRM != 0) {
 	    del_curterm(LAST_TRM);
+	    _nc_tgetent_leak(LAST_TRM);
+	}
     }
 }
 #endif
diff --git a/ncurses/tinfo/lib_termname.c b/ncurses/tinfo/lib_termname.c
index e3f6827..2d80bb3 100644
--- a/ncurses/tinfo/lib_termname.c
+++ b/ncurses/tinfo/lib_termname.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2003,2009 Free Software Foundation, Inc.              *
+ * Copyright 2020 Thomas E. Dickey                                          *
+ * Copyright 1998-2003,2009 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            *
@@ -28,7 +29,7 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_termname.c,v 1.12 2009/10/24 21:56:58 tom Exp $")
+MODULE_ID("$Id: lib_termname.c,v 1.13 2020/02/02 23:34:34 tom Exp $")
 
 NCURSES_EXPORT(char *)
 NCURSES_SP_NAME(termname) (NCURSES_SP_DCL0)
diff --git a/ncurses/tinfo/lib_tgoto.c b/ncurses/tinfo/lib_tgoto.c
index 31daf44..58b561f 100644
--- a/ncurses/tinfo/lib_tgoto.c
+++ b/ncurses/tinfo/lib_tgoto.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2000-2008,2012 Free Software Foundation, Inc.              *
+ * Copyright 2018-2020,2023 Thomas E. Dickey                                *
+ * Copyright 2000-2008,2012 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            *
@@ -35,7 +36,7 @@
 #include <ctype.h>
 #include <termcap.h>
 
-MODULE_ID("$Id: lib_tgoto.c,v 1.16 2012/02/24 02:08:08 tom Exp $")
+MODULE_ID("$Id: lib_tgoto.c,v 1.23 2023/04/16 17:19:40 tom Exp $")
 
 #if !PURE_TERMINFO
 static bool
@@ -125,7 +126,14 @@
 			*value += 1;
 			need_BC = TRUE;
 		    } else {
-			*value = 0200;	/* tputs will treat this as \0 */
+			/* tputs will pretend this is \0, which will almost
+			 * always work since ANSI-compatible terminals ignore
+			 * the character.  ECMA-48 does not document a C1
+			 * control for this value.  A few (obsolete) terminals
+			 * can use this value in special cases, such as cursor
+			 * addressing using single-byte coordinates.
+			 */
+			*value = 0200;
 		    }
 		}
 		result[used++] = (char) *value++;
@@ -199,6 +207,16 @@
 	result = tgoto_internal(string, x, y);
     else
 #endif
-	result = TPARM_2((NCURSES_CONST char *) string, y, x);
+    if ((result = TIPARM_2(string, y, x)) == NULL) {
+	/*
+	 * Because termcap did not provide a more general solution such as
+	 * tparm(), it was necessary to handle single-parameter capabilities
+	 * using tgoto().  The internal _nc_tiparm() function returns a NULL
+	 * for that case; retry for the single-parameter case.
+	 */
+	if ((result = TIPARM_1(string, y)) == NULL) {
+	    result = TIPARM_0(string);
+	}
+    }
     returnPtr(result);
 }
diff --git a/ncurses/tinfo/lib_ti.c b/ncurses/tinfo/lib_ti.c
index e9ae746..5cb77b8 100644
--- a/ncurses/tinfo/lib_ti.c
+++ b/ncurses/tinfo/lib_ti.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2010,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018,2020 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            *
@@ -36,7 +37,7 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: lib_ti.c,v 1.30 2013/06/08 16:55:05 tom Exp $")
+MODULE_ID("$Id: lib_ti.c,v 1.34 2020/02/02 23:34:34 tom Exp $")
 
 #if 0
 static bool
@@ -50,16 +51,16 @@
 #endif
 
 NCURSES_EXPORT(int)
-NCURSES_SP_NAME(tigetflag) (NCURSES_SP_DCLx NCURSES_CONST char *str)
+NCURSES_SP_NAME(tigetflag) (NCURSES_SP_DCLx const char *str)
 {
     int result = ABSENT_BOOLEAN;
-    int j = -1;
 
     T((T_CALLED("tigetflag(%p, %s)"), (void *) SP_PARM, str));
 
     if (HasTInfoTerminal(SP_PARM)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(str, BOOLEAN, FALSE);
 	if (entry_ptr != 0) {
@@ -88,23 +89,23 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(int)
-tigetflag(NCURSES_CONST char *str)
+tigetflag(const char *str)
 {
     return NCURSES_SP_NAME(tigetflag) (CURRENT_SCREEN, str);
 }
 #endif
 
 NCURSES_EXPORT(int)
-NCURSES_SP_NAME(tigetnum) (NCURSES_SP_DCLx NCURSES_CONST char *str)
+NCURSES_SP_NAME(tigetnum) (NCURSES_SP_DCLx const char *str)
 {
-    int j = -1;
     int result = CANCELLED_NUMERIC;	/* Solaris returns a -1 on error */
 
     T((T_CALLED("tigetnum(%p, %s)"), (void *) SP_PARM, str));
 
     if (HasTInfoTerminal(SP_PARM)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(str, NUMBER, FALSE);
 	if (entry_ptr != 0) {
@@ -135,23 +136,23 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(int)
-tigetnum(NCURSES_CONST char *str)
+tigetnum(const char *str)
 {
     return NCURSES_SP_NAME(tigetnum) (CURRENT_SCREEN, str);
 }
 #endif
 
 NCURSES_EXPORT(char *)
-NCURSES_SP_NAME(tigetstr) (NCURSES_SP_DCLx NCURSES_CONST char *str)
+NCURSES_SP_NAME(tigetstr) (NCURSES_SP_DCLx const char *str)
 {
     char *result = CANCELLED_STRING;
-    int j = -1;
 
     T((T_CALLED("tigetstr(%p, %s)"), (void *) SP_PARM, str));
 
     if (HasTInfoTerminal(SP_PARM)) {
-	TERMTYPE *tp = &(TerminalOf(SP_PARM)->type);
+	TERMTYPE2 *tp = &TerminalType(TerminalOf(SP_PARM));
 	struct name_table_entry const *entry_ptr;
+	int j = -1;
 
 	entry_ptr = _nc_find_type_entry(str, STRING, FALSE);
 	if (entry_ptr != 0) {
@@ -180,7 +181,7 @@
 
 #if NCURSES_SP_FUNCS
 NCURSES_EXPORT(char *)
-tigetstr(NCURSES_CONST char *str)
+tigetstr(const char *str)
 {
     return NCURSES_SP_NAME(tigetstr) (CURRENT_SCREEN, str);
 }
diff --git a/ncurses/tinfo/lib_tparm.c b/ncurses/tinfo/lib_tparm.c
index 4f18859..5666b27 100644
--- a/ncurses/tinfo/lib_tparm.c
+++ b/ncurses/tinfo/lib_tparm.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc.              *
+ * Copyright 2018-2021,2023 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            *
@@ -37,12 +38,22 @@
  *
  */
 
+#define entry _ncu_entry
+#define ENTRY _ncu_ENTRY
+
 #include <curses.priv.h>
 
+#undef entry
+#undef ENTRY
+
+#if HAVE_TSEARCH
+#include <search.h>
+#endif
+
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: lib_tparm.c,v 1.94 2015/07/17 01:03:35 tom Exp $")
+MODULE_ID("$Id: lib_tparm.c,v 1.153 2023/11/04 19:28:41 tom Exp $")
 
 /*
  *	char *
@@ -106,128 +117,245 @@
 
 NCURSES_EXPORT_VAR(int) _nc_tparm_err = 0;
 
-#define TPS(var) _nc_prescreen.tparm_state.var
+#define TPS(var) tps->var
 #define popcount _nc_popcount	/* workaround for NetBSD 6.0 defect */
 
-#if NO_LEAKS
-NCURSES_EXPORT(void)
-_nc_free_tparm(void)
+#define get_tparm_state(term) \
+	    (term != NULL \
+	      ? &(term->tparm_state) \
+	      : &(_nc_prescreen.tparm_state))
+
+#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
+#define tc_BUMP()  if (level < 0 && number < 2) number++
+
+typedef struct {
+    const char *format;		/* format-string can be used as cache-key */
+    int tparm_type;		/* bit-set for each string-parameter */
+    int num_actual;
+    int num_parsed;
+    int num_popped;
+    TPARM_ARG param[NUM_PARM];
+    char *p_is_s[NUM_PARM];
+} TPARM_DATA;
+
+#if HAVE_TSEARCH
+#define MyCache _nc_globals.cached_tparm
+#define MyCount _nc_globals.count_tparm
+static int which_tparm;
+static TPARM_DATA **delete_tparm;
+#endif /* HAVE_TSEARCH */
+
+static char dummy[] = "";	/* avoid const-cast */
+
+#if HAVE_TSEARCH
+static int
+cmp_format(const void *p, const void *q)
 {
-    if (TPS(out_buff) != 0) {
-	FreeAndNull(TPS(out_buff));
-	TPS(out_size) = 0;
-	TPS(out_used) = 0;
-	FreeAndNull(TPS(fmt_buff));
-	TPS(fmt_size) = 0;
+    const char *a = *(char *const *) p;
+    const char *b = *(char *const *) q;
+    return strcmp(a, b);
+}
+#endif
+
+#if HAVE_TSEARCH
+static void
+visit_nodes(const void *nodep, VISIT which, int depth)
+{
+    (void) depth;
+    if (which == preorder || which == leaf) {
+	delete_tparm[which_tparm] = *(TPARM_DATA **) nodep;
+	which_tparm++;
     }
 }
 #endif
 
-static NCURSES_INLINE void
-get_space(size_t need)
+NCURSES_EXPORT(void)
+_nc_free_tparm(TERMINAL *termp)
 {
-    need += TPS(out_used);
-    if (need > TPS(out_size)) {
-	TPS(out_size) = need * 2;
-	TYPE_REALLOC(char, TPS(out_size), TPS(out_buff));
+    TPARM_STATE *tps = get_tparm_state(termp);
+#if HAVE_TSEARCH
+    if (MyCount != 0) {
+	delete_tparm = typeCalloc(TPARM_DATA *, MyCount);
+	if (delete_tparm != NULL) {
+	    which_tparm = 0;
+	    twalk(MyCache, visit_nodes);
+	    for (which_tparm = 0; which_tparm < MyCount; ++which_tparm) {
+		TPARM_DATA *ptr = delete_tparm[which_tparm];
+		if (ptr != NULL) {
+		    tdelete(ptr, &MyCache, cmp_format);
+		    free((char *) ptr->format);
+		    free(ptr);
+		}
+	    }
+	    which_tparm = 0;
+	    twalk(MyCache, visit_nodes);
+	    FreeAndNull(delete_tparm);
+	}
+	MyCount = 0;
+	which_tparm = 0;
     }
+#endif
+    FreeAndNull(TPS(out_buff));
+    TPS(out_size) = 0;
+    TPS(out_used) = 0;
+
+    FreeAndNull(TPS(fmt_buff));
+    TPS(fmt_size) = 0;
 }
 
-static NCURSES_INLINE void
-save_text(const char *fmt, const char *s, int len)
+static int
+tparm_error(TPARM_STATE *tps, const char *message)
 {
-    size_t s_len = strlen(s);
-    if (len > (int) s_len)
-	s_len = (size_t) len;
-
-    get_space(s_len + 1);
-
-    _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
-		_nc_SLIMIT(TPS(out_size) - TPS(out_used))
-		fmt, s);
-    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
+    (void) tps;
+    (void) message;
+    DEBUG(2, ("%s: %s", message, _nc_visbuf(TPS(tparam_base))));
+    return ++_nc_tparm_err;
 }
 
+#define get_space(tps, need) \
+{ \
+    size_t need2get = need + TPS(out_used); \
+    if (need2get > TPS(out_size)) { \
+	TPS(out_size) = need2get * 2; \
+	TYPE_REALLOC(char, TPS(out_size), TPS(out_buff)); \
+    } \
+}
+
+#if NCURSES_EXPANDED
 static NCURSES_INLINE void
-save_number(const char *fmt, int number, int len)
-{
-    if (len < 30)
-	len = 30;		/* actually log10(MAX_INT)+1 */
-
-    get_space((size_t) len + 1);
-
-    _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
-		_nc_SLIMIT(TPS(out_size) - TPS(out_used))
-		fmt, number);
-    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
+  (get_space) (TPARM_STATE *tps, size_t need) {
+    get_space(tps, need);
 }
 
+#undef get_space
+#endif
+
+#define save_text(tps, fmt, s, len) \
+{ \
+    size_t s_len = (size_t) len + strlen(s) + strlen(fmt); \
+    get_space(tps, s_len + 1); \
+    _nc_SPRINTF(TPS(out_buff) + TPS(out_used), \
+		_nc_SLIMIT(TPS(out_size) - TPS(out_used)) \
+		fmt, s); \
+    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); \
+}
+
+#if NCURSES_EXPANDED
 static NCURSES_INLINE void
-save_char(int c)
-{
-    if (c == 0)
-	c = 0200;
-    get_space((size_t) 1);
-    TPS(out_buff)[TPS(out_used)++] = (char) c;
+  (save_text) (TPARM_STATE *tps, const char *fmt, const char *s, int len) {
+    save_text(tps, fmt, s, len);
 }
 
+#undef save_text
+#endif
+
+#define save_number(tps, fmt, number, len) \
+{ \
+    size_t s_len = (size_t) len + 30 + strlen(fmt); \
+    get_space(tps, s_len + 1); \
+    _nc_SPRINTF(TPS(out_buff) + TPS(out_used), \
+		_nc_SLIMIT(TPS(out_size) - TPS(out_used)) \
+		fmt, number); \
+    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used)); \
+}
+
+#if NCURSES_EXPANDED
 static NCURSES_INLINE void
-npush(int x)
-{
-    if (TPS(stack_ptr) < STACKSIZE) {
-	TPS(stack)[TPS(stack_ptr)].num_type = TRUE;
-	TPS(stack)[TPS(stack_ptr)].data.num = x;
-	TPS(stack_ptr)++;
-    } else {
-	DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
-	_nc_tparm_err++;
-    }
+  (save_number) (TPARM_STATE *tps, const char *fmt, int number, int len) {
+    save_number(tps, fmt, number, len);
 }
 
+#undef save_number
+#endif
+
+#define save_char(tps, c) \
+{ \
+    get_space(tps, (size_t) 1); \
+    TPS(out_buff)[TPS(out_used)++] = (char) ((c == 0) ? 0200 : c); \
+}
+
+#if NCURSES_EXPANDED
+static NCURSES_INLINE void
+  (save_char) (TPARM_STATE *tps, int c) {
+    save_char(tps, c);
+}
+
+#undef save_char
+#endif
+
+#define npush(tps, x) \
+{ \
+    if (TPS(stack_ptr) < STACKSIZE) { \
+	TPS(stack)[TPS(stack_ptr)].num_type = TRUE; \
+	TPS(stack)[TPS(stack_ptr)].data.num = x; \
+	TPS(stack_ptr)++; \
+    } else { \
+	(void) tparm_error(tps, "npush: stack overflow"); \
+    } \
+}
+
+#if NCURSES_EXPANDED
+static NCURSES_INLINE void
+  (npush) (TPARM_STATE *tps, int x) {
+    npush(tps, x);
+}
+
+#undef npush
+#endif
+
+#define spush(tps, x) \
+{ \
+    if (TPS(stack_ptr) < STACKSIZE) { \
+	TPS(stack)[TPS(stack_ptr)].num_type = FALSE; \
+	TPS(stack)[TPS(stack_ptr)].data.str = x; \
+	TPS(stack_ptr)++; \
+    } else { \
+	(void) tparm_error(tps, "spush: stack overflow"); \
+    } \
+}
+
+#if NCURSES_EXPANDED
+static NCURSES_INLINE void
+  (spush) (TPARM_STATE *tps, char *x) {
+    spush(tps, x);
+}
+
+#undef spush
+#endif
+
+#define npop(tps) \
+    ((TPS(stack_ptr)-- > 0) \
+     ? ((TPS(stack)[TPS(stack_ptr)].num_type) \
+	 ? TPS(stack)[TPS(stack_ptr)].data.num \
+	 : 0) \
+     : (tparm_error(tps, "npop: stack underflow"), \
+        TPS(stack_ptr) = 0))
+
+#if NCURSES_EXPANDED
 static NCURSES_INLINE int
-npop(void)
-{
-    int result = 0;
-    if (TPS(stack_ptr) > 0) {
-	TPS(stack_ptr)--;
-	if (TPS(stack)[TPS(stack_ptr)].num_type)
-	    result = TPS(stack)[TPS(stack_ptr)].data.num;
-    } else {
-	DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
-	_nc_tparm_err++;
-    }
-    return result;
+  (npop) (TPARM_STATE *tps) {
+    return npop(tps);
 }
+#undef npop
+#endif
 
-static NCURSES_INLINE void
-spush(char *x)
-{
-    if (TPS(stack_ptr) < STACKSIZE) {
-	TPS(stack)[TPS(stack_ptr)].num_type = FALSE;
-	TPS(stack)[TPS(stack_ptr)].data.str = x;
-	TPS(stack_ptr)++;
-    } else {
-	DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
-	_nc_tparm_err++;
-    }
-}
+#define spop(tps) \
+    ((TPS(stack_ptr)-- > 0) \
+     ? ((!TPS(stack)[TPS(stack_ptr)].num_type \
+        && TPS(stack)[TPS(stack_ptr)].data.str != 0) \
+         ? TPS(stack)[TPS(stack_ptr)].data.str \
+         : dummy) \
+     : (tparm_error(tps, "spop: stack underflow"), \
+        dummy))
 
+#if NCURSES_EXPANDED
 static NCURSES_INLINE char *
-spop(void)
-{
-    static char dummy[] = "";	/* avoid const-cast */
-    char *result = dummy;
-    if (TPS(stack_ptr) > 0) {
-	TPS(stack_ptr)--;
-	if (!TPS(stack)[TPS(stack_ptr)].num_type
-	    && TPS(stack)[TPS(stack_ptr)].data.str != 0)
-	    result = TPS(stack)[TPS(stack_ptr)].data.str;
-    } else {
-	DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
-	_nc_tparm_err++;
-    }
-    return result;
+  (spop) (TPARM_STATE *tps) {
+    return spop(tps);
 }
+#undef spop
+#endif
 
 static NCURSES_INLINE const char *
 parse_format(const char *s, char *format, int *len)
@@ -324,9 +452,6 @@
     return s;
 }
 
-#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
-#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
-
 /*
  * Analyze the string to see how many parameters we need from the varargs list,
  * and what their types are.  We will only accept string parameters if they
@@ -339,21 +464,22 @@
  * may be cases that we cannot see the explicit parameter numbers.
  */
 NCURSES_EXPORT(int)
-_nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
+_nc_tparm_analyze(TERMINAL *term, const char *string, char **p_is_s, int *popcount)
 {
+    TPARM_STATE *tps = get_tparm_state(term);
     size_t len2;
     int i;
     int lastpop = -1;
     int len;
     int number = 0;
+    int level = -1;
     const char *cp = string;
-    static char dummy[] = "";
 
     if (cp == 0)
 	return 0;
 
-    if ((len2 = strlen(cp)) > TPS(fmt_size)) {
-	TPS(fmt_size) = len2 + TPS(fmt_size) + 2;
+    if ((len2 = strlen(cp)) + 2 > TPS(fmt_size)) {
+	TPS(fmt_size) += len2 + 2;
 	TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
 	if (TPS(fmt_buff) == 0)
 	    return 0;
@@ -378,22 +504,27 @@
 #ifdef EXP_XTERM_1005
 	    case 'u':
 #endif
-		if (lastpop <= 0)
-		    number++;
+		if (lastpop <= 0) {
+		    tc_BUMP();
+		}
+		level -= 1;
 		lastpop = -1;
 		break;
 
 	    case 'l':
 	    case 's':
-		if (lastpop > 0)
+		if (lastpop > 0) {
+		    level -= 1;
 		    p_is_s[lastpop - 1] = dummy;
-		++number;
+		}
+		tc_BUMP();
 		break;
 
 	    case 'p':
 		cp++;
 		i = (UChar(*cp) - '0');
 		if (i >= 0 && i <= NUM_PARM) {
+		    ++level;
 		    lastpop = i;
 		    if (lastpop > *popcount)
 			*popcount = lastpop;
@@ -401,20 +532,22 @@
 		break;
 
 	    case 'P':
-		++number;
 		++cp;
 		break;
 
 	    case 'g':
+		++level;
 		cp++;
 		break;
 
 	    case S_QUOTE:
+		++level;
 		cp += 2;
 		lastpop = -1;
 		break;
 
 	    case L_BRACE:
+		++level;
 		cp++;
 		while (isdigit(UChar(*cp))) {
 		    cp++;
@@ -434,14 +567,15 @@
 	    case '=':
 	    case '<':
 	    case '>':
+		tc_BUMP();
+		level -= 1;	/* pop 2, operate, push 1 */
 		lastpop = -1;
-		number += 2;
 		break;
 
 	    case '!':
 	    case '~':
+		tc_BUMP();
 		lastpop = -1;
-		++number;
 		break;
 
 	    case 'i':
@@ -458,100 +592,220 @@
     return number;
 }
 
-static NCURSES_INLINE char *
-tparam_internal(int use_TPARM_ARG, const char *string, va_list ap)
+/*
+ * Analyze the capability string, finding the number of parameters and their
+ * types.
+ *
+ * TODO: cache the result so that this is done once per capability per term.
+ */
+static int
+tparm_setup(TERMINAL *term, const char *string, TPARM_DATA *result)
 {
-    char *p_is_s[NUM_PARM];
-    TPARM_ARG param[NUM_PARM];
-    int popcount = 0;
+    TPARM_STATE *tps = get_tparm_state(term);
+    int rc = OK;
+
+    TPS(out_used) = 0;
+    memset(result, 0, sizeof(*result));
+
+    if (!VALID_STRING(string)) {
+	TR(TRACE_CALLS, ("%s: format is invalid", TPS(tname)));
+	rc = ERR;
+    } else {
+#if HAVE_TSEARCH
+	TPARM_DATA *fs;
+	void *ft;
+
+	result->format = string;
+	if ((ft = tfind(result, &MyCache, cmp_format)) != 0) {
+	    size_t len2;
+	    fs = *(TPARM_DATA **) ft;
+	    *result = *fs;
+	    if ((len2 = strlen(string)) + 2 > TPS(fmt_size)) {
+		TPS(fmt_size) += len2 + 2;
+		TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
+		if (TPS(fmt_buff) == 0)
+		    return ERR;
+	    }
+	} else
+#endif
+	{
+	    /*
+	     * Find the highest parameter-number referred to in the format
+	     * string.  Use this value to limit the number of arguments copied
+	     * from the variable-length argument list.
+	     */
+	    result->num_parsed = _nc_tparm_analyze(term, string,
+						   result->p_is_s,
+						   &(result->num_popped));
+	    if (TPS(fmt_buff) == 0) {
+		TR(TRACE_CALLS, ("%s: error in analysis", TPS(tname)));
+		rc = ERR;
+	    } else {
+		int n;
+
+		if (result->num_parsed > NUM_PARM)
+		    result->num_parsed = NUM_PARM;
+		if (result->num_popped > NUM_PARM)
+		    result->num_popped = NUM_PARM;
+		result->num_actual = Max(result->num_popped, result->num_parsed);
+
+		for (n = 0; n < result->num_actual; ++n) {
+		    if (result->p_is_s[n])
+			result->tparm_type |= (1 << n);
+		}
+#if HAVE_TSEARCH
+		if ((fs = typeCalloc(TPARM_DATA, 1)) != 0) {
+		    *fs = *result;
+		    if ((fs->format = strdup(string)) != 0) {
+			if (tsearch(fs, &MyCache, cmp_format) != 0) {
+			    ++MyCount;
+			} else {
+			    free(fs);
+			    rc = ERR;
+			}
+		    } else {
+			free(fs);
+			rc = ERR;
+		    }
+		} else {
+		    rc = ERR;
+		}
+#endif
+	    }
+	}
+    }
+
+    return rc;
+}
+
+/*
+ * A few caps (such as plab_norm) have string-valued parms.  We'll have to
+ * assume that the caller knows the difference, since a char* and an int may
+ * not be the same size on the stack.  The normal prototype for tparm uses 9
+ * long's, which is consistent with our va_arg() usage.
+ */
+static void
+tparm_copy_valist(TPARM_DATA *data, int use_TPARM_ARG, va_list ap)
+{
+    int i;
+
+    for (i = 0; i < data->num_actual; i++) {
+	if (data->p_is_s[i] != 0) {
+	    char *value = va_arg(ap, char *);
+	    if (value == 0)
+		value = dummy;
+	    data->p_is_s[i] = value;
+	    data->param[i] = 0;
+	} else if (use_TPARM_ARG) {
+	    data->param[i] = va_arg(ap, TPARM_ARG);
+	} else {
+	    data->param[i] = (TPARM_ARG) va_arg(ap, int);
+	}
+    }
+}
+
+/*
+ * This is a termcap compatibility hack.  If there are no explicit pop
+ * operations in the string, load the stack in such a way that successive pops
+ * will grab successive parameters.  That will make the expansion of (for
+ * example) \E[%d;%dH work correctly in termcap style, which means tparam()
+ * will expand termcap strings OK.
+ */
+static bool
+tparm_tc_compat(TPARM_STATE *tps, TPARM_DATA *data)
+{
+    bool termcap_hack = FALSE;
+
+    TPS(stack_ptr) = 0;
+
+    if (data->num_popped == 0) {
+	int i;
+
+	termcap_hack = TRUE;
+	for (i = data->num_parsed - 1; i >= 0; i--) {
+	    if (data->p_is_s[i]) {
+		spush(tps, data->p_is_s[i]);
+	    } else {
+		npush(tps, (int) data->param[i]);
+	    }
+	}
+    }
+    return termcap_hack;
+}
+
+#ifdef TRACE
+static void
+tparm_trace_call(TPARM_STATE *tps, const char *string, TPARM_DATA *data)
+{
+    if (USE_TRACEF(TRACE_CALLS)) {
+	int i;
+	for (i = 0; i < data->num_actual; i++) {
+	    if (data->p_is_s[i] != 0) {
+		save_text(tps, ", %s", _nc_visbuf(data->p_is_s[i]), 0);
+	    } else if ((long) data->param[i] > MAX_OF_TYPE(NCURSES_INT2) ||
+		       (long) data->param[i] < 0) {
+		_tracef("BUG: problem with tparm parameter #%d of %d",
+			i + 1, data->num_actual);
+		break;
+	    } else {
+		save_number(tps, ", %d", (int) data->param[i], 0);
+	    }
+	}
+	_tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(string), TPS(out_buff));
+	TPS(out_used) = 0;
+	_nc_unlock_global(tracef);
+    }
+}
+
+#else
+#define tparm_trace_call(tps, string, data)	/* nothing */
+#endif /* TRACE */
+
+#define init_vars(name) \
+	if (!name##_used) { \
+	    name##_used = TRUE; \
+	    memset(name##_vars, 0, sizeof(name##_vars)); \
+	}
+
+static NCURSES_INLINE char *
+tparam_internal(TPARM_STATE *tps, const char *string, TPARM_DATA *data)
+{
     int number;
-    int num_args;
     int len;
     int level;
     int x, y;
     int i;
+    const char *s;
     const char *cp = string;
-    size_t len2;
-    bool termcap_hack;
-    bool incremented_two;
-
-    if (cp == NULL)
-	return NULL;
-
-    TPS(out_used) = 0;
-    len2 = strlen(cp);
-
+    size_t len2 = strlen(cp);
+    bool incremented_two = FALSE;
+    bool termcap_hack = tparm_tc_compat(tps, data);
     /*
-     * Find the highest parameter-number referred to in the format string.
-     * Use this value to limit the number of arguments copied from the
-     * variable-length argument list.
+     * SVr4 curses stores variables 'A' to 'Z' in the TERMINAL structure (so
+     * they are initialized once to zero), and variables 'a' to 'z' on the
+     * stack in tparm, referring to the former as "static" and the latter as
+     * "dynamic".  However, it makes no check to ensure that the "dynamic"
+     * variables are initialized.
+     *
+     * Solaris xpg4 curses makes no distinction between the upper/lower, and
+     * stores the common set of 26 variables on the stack, without initializing
+     * them.
+     *
+     * In ncurses, both sets of variables are initialized on the first use.
      */
-    number = _nc_tparm_analyze(cp, p_is_s, &popcount);
-    if (TPS(fmt_buff) == 0)
+    bool dynamic_used = FALSE;
+    int dynamic_vars[NUM_VARS];
+
+    tparm_trace_call(tps, string, data);
+
+    if (TPS(fmt_buff) == NULL) {
+	T((T_RETURN("<null>")));
 	return NULL;
-
-    incremented_two = FALSE;
-
-    if (number > NUM_PARM)
-	number = NUM_PARM;
-    if (popcount > NUM_PARM)
-	popcount = NUM_PARM;
-    num_args = max(popcount, number);
-
-    for (i = 0; i < num_args; i++) {
-	/*
-	 * A few caps (such as plab_norm) have string-valued parms.
-	 * We'll have to assume that the caller knows the difference, since
-	 * a char* and an int may not be the same size on the stack.  The
-	 * normal prototype for this uses 9 long's, which is consistent with
-	 * our va_arg() usage.
-	 */
-	if (p_is_s[i] != 0) {
-	    p_is_s[i] = va_arg(ap, char *);
-	    param[i] = 0;
-	} else if (use_TPARM_ARG) {
-	    param[i] = va_arg(ap, TPARM_ARG);
-	} else {
-	    param[i] = (TPARM_ARG) va_arg(ap, int);
-	}
     }
 
-    /*
-     * This is a termcap compatibility hack.  If there are no explicit pop
-     * operations in the string, load the stack in such a way that
-     * successive pops will grab successive parameters.  That will make
-     * the expansion of (for example) \E[%d;%dH work correctly in termcap
-     * style, which means tparam() will expand termcap strings OK.
-     */
-    TPS(stack_ptr) = 0;
-    termcap_hack = FALSE;
-    if (popcount == 0) {
-	termcap_hack = TRUE;
-	popcount = number;
-	for (i = number - 1; i >= 0; i--) {
-	    if (p_is_s[i])
-		spush(p_is_s[i]);
-	    else
-		npush((int) param[i]);
-	}
-    }
-#ifdef TRACE
-    if (USE_TRACEF(TRACE_CALLS)) {
-	for (i = 0; i < num_args; i++) {
-	    if (p_is_s[i] != 0)
-		save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
-	    else
-		save_number(", %d", (int) param[i], 0);
-	}
-	_tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff));
-	TPS(out_used) = 0;
-	_nc_unlock_global(tracef);
-    }
-#endif /* TRACE */
-
     while ((cp - string) < (int) len2) {
 	if (*cp != '%') {
-	    save_char(UChar(*cp));
+	    save_char(tps, UChar(*cp));
 	} else {
 	    TPS(tparam_base) = cp++;
 	    cp = parse_format(cp, TPS(fmt_buff), &len);
@@ -559,50 +813,54 @@
 	    default:
 		break;
 	    case '%':
-		save_char('%');
+		save_char(tps, '%');
 		break;
 
 	    case 'd':		/* FALLTHRU */
 	    case 'o':		/* FALLTHRU */
 	    case 'x':		/* FALLTHRU */
 	    case 'X':		/* FALLTHRU */
-		save_number(TPS(fmt_buff), npop(), len);
+		x = npop(tps);
+		save_number(tps, TPS(fmt_buff), x, len);
 		break;
 
 	    case 'c':		/* FALLTHRU */
-		save_char(npop());
+		x = npop(tps);
+		save_char(tps, x);
 		break;
 
 #ifdef EXP_XTERM_1005
 	    case 'u':
 		{
 		    unsigned char target[10];
-		    unsigned source = (unsigned) npop();
+		    unsigned source = (unsigned) npop(tps);
 		    int rc = _nc_conv_to_utf8(target, source, (unsigned)
 					      sizeof(target));
 		    int n;
 		    for (n = 0; n < rc; ++n) {
-			save_char(target[n]);
+			save_char(tps, target[n]);
 		    }
 		}
 		break;
 #endif
 	    case 'l':
-		npush((int) strlen(spop()));
+		s = spop(tps);
+		npush(tps, (int) strlen(s));
 		break;
 
 	    case 's':
-		save_text(TPS(fmt_buff), spop(), len);
+		s = spop(tps);
+		save_text(tps, TPS(fmt_buff), s, len);
 		break;
 
 	    case 'p':
 		cp++;
 		i = (UChar(*cp) - '1');
 		if (i >= 0 && i < NUM_PARM) {
-		    if (p_is_s[i]) {
-			spush(p_is_s[i]);
+		    if (data->p_is_s[i]) {
+			spush(tps, data->p_is_s[i]);
 		    } else {
-			npush((int) param[i]);
+			npush(tps, (int) data->param[i]);
 		    }
 		}
 		break;
@@ -611,10 +869,11 @@
 		cp++;
 		if (isUPPER(*cp)) {
 		    i = (UChar(*cp) - 'A');
-		    TPS(static_vars)[i] = npop();
+		    TPS(static_vars)[i] = npop(tps);
 		} else if (isLOWER(*cp)) {
 		    i = (UChar(*cp) - 'a');
-		    TPS(dynamic_var)[i] = npop();
+		    init_vars(dynamic);
+		    dynamic_vars[i] = npop(tps);
 		}
 		break;
 
@@ -622,16 +881,17 @@
 		cp++;
 		if (isUPPER(*cp)) {
 		    i = (UChar(*cp) - 'A');
-		    npush(TPS(static_vars)[i]);
+		    npush(tps, TPS(static_vars)[i]);
 		} else if (isLOWER(*cp)) {
 		    i = (UChar(*cp) - 'a');
-		    npush(TPS(dynamic_var)[i]);
+		    init_vars(dynamic);
+		    npush(tps, dynamic_vars[i]);
 		}
 		break;
 
 	    case S_QUOTE:
 		cp++;
-		npush(UChar(*cp));
+		npush(tps, UChar(*cp));
 		cp++;
 		break;
 
@@ -642,83 +902,95 @@
 		    number = (number * 10) + (UChar(*cp) - '0');
 		    cp++;
 		}
-		npush(number);
+		npush(tps, number);
 		break;
 
 	    case '+':
-		npush(npop() + npop());
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x + y);
 		break;
 
 	    case '-':
-		y = npop();
-		x = npop();
-		npush(x - y);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x - y);
 		break;
 
 	    case '*':
-		npush(npop() * npop());
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x * y);
 		break;
 
 	    case '/':
-		y = npop();
-		x = npop();
-		npush(y ? (x / y) : 0);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, y ? (x / y) : 0);
 		break;
 
 	    case 'm':
-		y = npop();
-		x = npop();
-		npush(y ? (x % y) : 0);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, y ? (x % y) : 0);
 		break;
 
 	    case 'A':
-		y = npop();
-		x = npop();
-		npush(y && x);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, y && x);
 		break;
 
 	    case 'O':
-		y = npop();
-		x = npop();
-		npush(y || x);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, y || x);
 		break;
 
 	    case '&':
-		npush(npop() & npop());
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x & y);
 		break;
 
 	    case '|':
-		npush(npop() | npop());
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x | y);
 		break;
 
 	    case '^':
-		npush(npop() ^ npop());
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x ^ y);
 		break;
 
 	    case '=':
-		y = npop();
-		x = npop();
-		npush(x == y);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x == y);
 		break;
 
 	    case '<':
-		y = npop();
-		x = npop();
-		npush(x < y);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x < y);
 		break;
 
 	    case '>':
-		y = npop();
-		x = npop();
-		npush(x > y);
+		y = npop(tps);
+		x = npop(tps);
+		npush(tps, x > y);
 		break;
 
 	    case '!':
-		npush(!npop());
+		x = npop(tps);
+		npush(tps, !x);
 		break;
 
 	    case '~':
-		npush(~npop());
+		x = npop(tps);
+		npush(tps, ~x);
 		break;
 
 	    case 'i':
@@ -731,15 +1003,15 @@
 		 */
 		if (!incremented_two) {
 		    incremented_two = TRUE;
-		    if (p_is_s[0] == 0) {
-			param[0]++;
+		    if (data->p_is_s[0] == 0) {
+			data->param[0]++;
 			if (termcap_hack)
-			    TPS(stack)[0].data.num = (int) param[0];
+			    TPS(stack)[0].data.num = (int) data->param[0];
 		    }
-		    if (p_is_s[1] == 0) {
-			param[1]++;
+		    if (data->p_is_s[1] == 0) {
+			data->param[1]++;
 			if (termcap_hack)
-			    TPS(stack)[1].data.num = (int) param[1];
+			    TPS(stack)[1].data.num = (int) data->param[1];
 		    }
 		}
 		break;
@@ -748,7 +1020,7 @@
 		break;
 
 	    case 't':
-		x = npop();
+		x = npop(tps);
 		if (!x) {
 		    /* scan forward for %e or %; at level zero */
 		    cp++;
@@ -807,64 +1079,330 @@
 	cp++;
     }				/* endwhile (*cp) */
 
-    get_space((size_t) 1);
+    get_space(tps, (size_t) 1);
     TPS(out_buff)[TPS(out_used)] = '\0';
 
+    if (TPS(stack_ptr) && !_nc_tparm_err) {
+	DEBUG(2, ("tparm: stack has %d item%s on return",
+		  TPS(stack_ptr),
+		  TPS(stack_ptr) == 1 ? "" : "s"));
+	_nc_tparm_err++;
+    }
+
     T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
     return (TPS(out_buff));
 }
 
-#if NCURSES_TPARM_VARARGS
-#define tparm_varargs tparm
-#else
-#define tparm_proto tparm
+#ifdef CUR
+/*
+ * Only a few standard capabilities accept string parameters.  The others that
+ * are parameterized accept only numeric parameters.
+ */
+static bool
+check_string_caps(TPARM_DATA *data, const char *string)
+{
+    bool result = FALSE;
+
+#define CHECK_CAP(name) (VALID_STRING(name) && !strcmp(name, string))
+
+    /*
+     * Disallow string parameters unless we can check them against a terminal
+     * description.
+     */
+    if (cur_term != NULL) {
+	int want_type = 0;
+
+	if (CHECK_CAP(pkey_key))
+	    want_type = 2;	/* function key #1, type string #2 */
+	else if (CHECK_CAP(pkey_local))
+	    want_type = 2;	/* function key #1, execute string #2 */
+	else if (CHECK_CAP(pkey_xmit))
+	    want_type = 2;	/* function key #1, transmit string #2 */
+	else if (CHECK_CAP(plab_norm))
+	    want_type = 2;	/* label #1, show string #2 */
+#ifdef pkey_plab
+	else if (CHECK_CAP(pkey_plab))
+	    want_type = 6;	/* function key #1, type string #2, show string #3 */
+#endif
+#if NCURSES_XNAMES
+	else {
+	    char *check;
+
+	    check = tigetstr("Cs");
+	    if (CHECK_CAP(check))
+		want_type = 1;	/* style #1 */
+
+	    check = tigetstr("Ms");
+	    if (CHECK_CAP(check))
+		want_type = 3;	/* storage unit #1, content #2 */
+	}
 #endif
 
-NCURSES_EXPORT(char *)
-tparm_varargs(NCURSES_CONST char *string,...)
-{
-    va_list ap;
-    char *result;
-
-    _nc_tparm_err = 0;
-    va_start(ap, string);
-#ifdef TRACE
-    TPS(tname) = "tparm";
-#endif /* TRACE */
-    result = tparam_internal(TRUE, string, ap);
-    va_end(ap);
+	if (want_type == data->tparm_type) {
+	    result = TRUE;
+	} else {
+	    T(("unexpected string-parameter"));
+	}
+    }
     return result;
 }
 
-#if !NCURSES_TPARM_VARARGS
+#define ValidCap(allow_strings) (myData.tparm_type == 0 || \
+				 (allow_strings && \
+				  check_string_caps(&myData, string)))
+#else
+#define ValidCap(allow_strings) 1
+#endif
+
+#if NCURSES_TPARM_VARARGS
+
 NCURSES_EXPORT(char *)
-tparm_proto(NCURSES_CONST char *string,
-	    TPARM_ARG a1,
-	    TPARM_ARG a2,
-	    TPARM_ARG a3,
-	    TPARM_ARG a4,
-	    TPARM_ARG a5,
-	    TPARM_ARG a6,
-	    TPARM_ARG a7,
-	    TPARM_ARG a8,
-	    TPARM_ARG a9)
+tparm(const char *string, ...)
 {
-    return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    TPARM_DATA myData;
+    char *result = NULL;
+
+    _nc_tparm_err = 0;
+#ifdef TRACE
+    tps->tname = "tparm";
+#endif /* TRACE */
+
+    if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(TRUE)) {
+	va_list ap;
+
+	va_start(ap, string);
+	tparm_copy_valist(&myData, TRUE, ap);
+	va_end(ap);
+
+	result = tparam_internal(tps, string, &myData);
+    }
+    return result;
 }
+
+#else /* !NCURSES_TPARM_VARARGS */
+
+NCURSES_EXPORT(char *)
+tparm(const char *string,
+      TPARM_ARG a1,
+      TPARM_ARG a2,
+      TPARM_ARG a3,
+      TPARM_ARG a4,
+      TPARM_ARG a5,
+      TPARM_ARG a6,
+      TPARM_ARG a7,
+      TPARM_ARG a8,
+      TPARM_ARG a9)
+{
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    TPARM_DATA myData;
+    char *result = NULL;
+
+    _nc_tparm_err = 0;
+#ifdef TRACE
+    tps->tname = "tparm";
+#endif /* TRACE */
+
+#define string_ok (sizeof(char*) <= sizeof(TPARM_ARG))
+
+    if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(string_ok)) {
+
+	myData.param[0] = a1;
+	myData.param[1] = a2;
+	myData.param[2] = a3;
+	myData.param[3] = a4;
+	myData.param[4] = a5;
+	myData.param[5] = a6;
+	myData.param[6] = a7;
+	myData.param[7] = a8;
+	myData.param[8] = a9;
+
+	result = tparam_internal(tps, string, &myData);
+    }
+    return result;
+}
+
 #endif /* NCURSES_TPARM_VARARGS */
 
 NCURSES_EXPORT(char *)
-tiparm(const char *string,...)
+tiparm(const char *string, ...)
 {
-    va_list ap;
-    char *result;
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    TPARM_DATA myData;
+    char *result = NULL;
 
     _nc_tparm_err = 0;
-    va_start(ap, string);
 #ifdef TRACE
-    TPS(tname) = "tiparm";
+    tps->tname = "tiparm";
 #endif /* TRACE */
-    result = tparam_internal(FALSE, string, ap);
-    va_end(ap);
+
+    if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(TRUE)) {
+	va_list ap;
+
+	va_start(ap, string);
+	tparm_copy_valist(&myData, FALSE, ap);
+	va_end(ap);
+
+	result = tparam_internal(tps, string, &myData);
+    }
     return result;
 }
+
+/*
+ * Use tparm if the formatting string matches the expected number of parameters
+ * counting string-parameters.
+ */
+NCURSES_EXPORT(char *)
+tiparm_s(int num_expected, int tparm_type, const char *string, ...)
+{
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    TPARM_DATA myData;
+    char *result = NULL;
+
+    _nc_tparm_err = 0;
+#ifdef TRACE
+    tps->tname = "tiparm_s";
+#endif /* TRACE */
+    if (num_expected >= 0 &&
+	num_expected <= 9 &&
+	tparm_type >= 0 &&
+	tparm_type < 7 &&	/* limit to 2 string parameters */
+	tparm_setup(cur_term, string, &myData) == OK &&
+	myData.tparm_type == tparm_type &&
+	myData.num_actual == num_expected) {
+	va_list ap;
+
+	va_start(ap, string);
+	tparm_copy_valist(&myData, FALSE, ap);
+	va_end(ap);
+
+	result = tparam_internal(tps, string, &myData);
+    }
+    return result;
+}
+
+/*
+ * Analyze the formatting string, return the analysis.
+ */
+NCURSES_EXPORT(int)
+tiscan_s(int *num_expected, int *tparm_type, const char *string)
+{
+    TPARM_DATA myData;
+    int result = ERR;
+
+#ifdef TRACE
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    tps->tname = "tiscan_s";
+#endif /* TRACE */
+
+    if (tparm_setup(cur_term, string, &myData) == OK) {
+	*num_expected = myData.num_actual;
+	*tparm_type = myData.tparm_type;
+	result = OK;
+    }
+    return result;
+}
+
+/*
+ * The internal-use flavor ensures that parameters are numbers, not strings.
+ * In addition to ensuring that they are numbers, it ensures that the parameter
+ * count is consistent with intended usage.
+ *
+ * Unlike the general-purpose tparm/tiparm, these internal calls are fairly
+ * well defined:
+ *
+ * expected == 0 - not applicable
+ * expected == 1 - set color, or vertical/horizontal addressing
+ * expected == 2 - cursor addressing
+ * expected == 4 - initialize color or color pair
+ * expected == 9 - set attributes
+ *
+ * Only for the last case (set attributes) should a parameter be optional.
+ * Also, a capability which calls for more parameters than expected should be
+ * ignored.
+ *
+ * Return a null if the parameter-checks fail.  Otherwise, return a pointer to
+ * the formatted capability string.
+ */
+NCURSES_EXPORT(char *)
+_nc_tiparm(int expected, const char *string, ...)
+{
+    TPARM_STATE *tps = get_tparm_state(cur_term);
+    TPARM_DATA myData;
+    char *result = NULL;
+
+    _nc_tparm_err = 0;
+    T((T_CALLED("_nc_tiparm(%d, %s, ...)"), expected, _nc_visbuf(string)));
+#ifdef TRACE
+    tps->tname = "_nc_tiparm";
+#endif /* TRACE */
+
+    if (tparm_setup(cur_term, string, &myData) == OK && ValidCap(FALSE)) {
+#ifdef CUR
+	if (myData.num_actual != expected && cur_term != NULL) {
+	    int needed = expected;
+	    if (CHECK_CAP(to_status_line)) {
+		needed = 0;	/* allow for xterm's status line */
+	    } else if (CHECK_CAP(set_a_background)) {
+		needed = 0;	/* allow for monochrome fakers */
+	    } else if (CHECK_CAP(set_a_foreground)) {
+		needed = 0;
+	    } else if (CHECK_CAP(set_background)) {
+		needed = 0;
+	    } else if (CHECK_CAP(set_foreground)) {
+		needed = 0;
+	    }
+#if NCURSES_XNAMES
+	    else {
+		char *check;
+
+		check = tigetstr("xm");
+		if (CHECK_CAP(check)) {
+		    needed = 3;
+		}
+		check = tigetstr("S0");
+		if (CHECK_CAP(check)) {
+		    needed = 0;	/* used in screen-base */
+		}
+	    }
+#endif
+	    if (myData.num_actual >= needed && myData.num_actual <= expected)
+		expected = myData.num_actual;
+	}
+#endif
+	if (myData.num_actual == 0 && expected) {
+	    T(("missing parameter%s, expected %s%d",
+	       expected > 1 ? "s" : "",
+	       expected == 9 ? "up to " : "",
+	       expected));
+	} else if (myData.num_actual > expected) {
+	    T(("too many parameters, have %d, expected %d",
+	       myData.num_actual,
+	       expected));
+	} else if (expected != 9 && myData.num_actual != expected) {
+	    T(("expected %d parameters, have %d",
+	       myData.num_actual,
+	       expected));
+	} else {
+	    va_list ap;
+
+	    va_start(ap, string);
+	    tparm_copy_valist(&myData, FALSE, ap);
+	    va_end(ap);
+
+	    result = tparam_internal(tps, string, &myData);
+	}
+    }
+    returnPtr(result);
+}
+
+/*
+ * Improve tic's checks by resetting the terminfo "static variables" before
+ * calling functions which may update them.
+ */
+NCURSES_EXPORT(void)
+_nc_reset_tparm(TERMINAL *term)
+{
+    TPARM_STATE *tps = get_tparm_state(term);
+    memset(TPS(static_vars), 0, sizeof(TPS(static_vars)));
+}
diff --git a/ncurses/tinfo/lib_tputs.c b/ncurses/tinfo/lib_tputs.c
index 09cbbc2..f834532 100644
--- a/ncurses/tinfo/lib_tputs.c
+++ b/ncurses/tinfo/lib_tputs.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2013,2015 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 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            *
@@ -51,12 +52,12 @@
 #include <termcap.h>		/* ospeed */
 #include <tic.h>
 
-MODULE_ID("$Id: lib_tputs.c,v 1.96 2015/01/03 23:51:23 tom Exp $")
+MODULE_ID("$Id: lib_tputs.c,v 1.111 2023/09/16 16:05:15 tom Exp $")
 
 NCURSES_EXPORT_VAR(char) PC = 0;              /* used by termcap library */
 NCURSES_EXPORT_VAR(NCURSES_OSPEED) ospeed = 0;        /* used by termcap library */
 
-NCURSES_EXPORT_VAR(int) _nc_nulls_sent = 0;   /* used by 'tack' program */
+NCURSES_EXPORT_VAR(int) _nc_nulls_sent = 0;
 
 #if NCURSES_NO_PADDING
 NCURSES_EXPORT(void)
@@ -88,6 +89,9 @@
 {
     T((T_CALLED("delay_output(%p,%d)"), (void *) SP_PARM, ms));
 
+    if (ms > MAX_DELAY_MSECS)
+	ms = MAX_DELAY_MSECS;
+
     if (!HasTInfoTerminal(SP_PARM))
 	returnCode(ERR);
 
@@ -119,16 +123,20 @@
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(_nc_flush) (NCURSES_SP_DCL0)
 {
+    T((T_CALLED("_nc_flush(%p)"), (void *) SP_PARM));
     if (SP_PARM != 0 && SP_PARM->_ofd >= 0) {
+	TR(TRACE_CHARPUT, ("ofd:%d inuse:%lu buffer:%p",
+			   SP_PARM->_ofd,
+			   (unsigned long) SP_PARM->out_inuse,
+			   SP_PARM->out_buffer));
 	if (SP_PARM->out_inuse) {
 	    char *buf = SP_PARM->out_buffer;
-	    size_t amount = SP->out_inuse;
-	    ssize_t res;
+	    size_t amount = SP_PARM->out_inuse;
 
-	    SP->out_inuse = 0;
+	    TR(TRACE_CHARPUT, ("flushing %ld/%ld bytes",
+			       (unsigned long) amount, _nc_outchars));
 	    while (amount) {
-		res = write(SP_PARM->_ofd, buf, amount);
-
+		ssize_t res = write(SP_PARM->_ofd, buf, amount);
 		if (res > 0) {
 		    /* if the write was incomplete, try again */
 		    amount -= (size_t) res;
@@ -141,8 +149,17 @@
 		    break;	/* an error we can not recover from */
 		}
 	    }
+	} else if (SP_PARM->out_buffer == 0) {
+	    TR(TRACE_CHARPUT, ("flushing stdout"));
+	    fflush(stdout);
 	}
+    } else {
+	TR(TRACE_CHARPUT, ("flushing stdout"));
+	fflush(stdout);
     }
+    if (SP_PARM != 0)
+	SP_PARM->out_inuse = 0;
+    returnVoid;
 }
 
 #if NCURSES_SP_FUNCS
@@ -263,21 +280,24 @@
 			NCURSES_SP_OUTC outc)
 {
     NCURSES_SP_OUTC my_outch = GetOutCh();
-    bool always_delay;
-    bool normal_delay;
+    bool always_delay = FALSE;
+    bool normal_delay = FALSE;
     int number;
 #if BSD_TPUTS
     int trailpad;
 #endif /* BSD_TPUTS */
 
 #ifdef TRACE
-    char addrbuf[32];
-
     if (USE_TRACEF(TRACE_TPUTS)) {
-	if (outc == NCURSES_SP_NAME(_nc_outch))
+	char addrbuf[32];
+	TR_FUNC_BFR(1);
+
+	if (outc == NCURSES_SP_NAME(_nc_outch)) {
 	    _nc_STRCPY(addrbuf, "_nc_outch", sizeof(addrbuf));
-	else
-	    _nc_SPRINTF(addrbuf, _nc_SLIMIT(sizeof(addrbuf)) "%p", outc);
+	} else {
+	    _nc_SPRINTF(addrbuf, _nc_SLIMIT(sizeof(addrbuf)) "%s",
+			TR_FUNC_ARG(0, outc));
+	}
 	if (_nc_tputs_trace) {
 	    _tracef("tputs(%s = %s, %d, %s) called", _nc_tputs_trace,
 		    _nc_visbuf(string), affcnt, addrbuf);
@@ -289,32 +309,30 @@
     }
 #endif /* TRACE */
 
-    if (SP_PARM != 0 && !HasTInfoTerminal(SP_PARM))
-	return ERR;
-
     if (!VALID_STRING(string))
 	return ERR;
 
-    if (
+    if (SP_PARM != 0 && HasTInfoTerminal(SP_PARM)) {
+	if (
 #if NCURSES_SP_FUNCS
-	   (SP_PARM != 0 && SP_PARM->_term == 0)
+	       (SP_PARM != 0 && SP_PARM->_term == 0)
 #else
-	   cur_term == 0
+	       cur_term == 0
 #endif
-	) {
-	always_delay = FALSE;
-	normal_delay = TRUE;
-    } else {
-	always_delay = (string == bell) || (string == flash_screen);
-	normal_delay =
-	    !xon_xoff
-	    && padding_baud_rate
+	    ) {
+	    always_delay = FALSE;
+	    normal_delay = TRUE;
+	} else {
+	    always_delay = (string == bell) || (string == flash_screen);
+	    normal_delay =
+		!xon_xoff
+		&& padding_baud_rate
 #if NCURSES_NO_PADDING
-	    && !GetNoPadding(SP_PARM)
+		&& !GetNoPadding(SP_PARM)
 #endif
-	    && (_nc_baudrate(ospeed) >= padding_baud_rate);
+		&& (_nc_baudrate(ospeed) >= padding_baud_rate);
+	}
     }
-
 #if BSD_TPUTS
     /*
      * This ugly kluge deals with the fact that some ancient BSD programs
@@ -413,7 +431,7 @@
      */
     if (trailpad > 0
 	&& (always_delay || normal_delay))
-	delay_output(trailpad / 10);
+	NCURSES_SP_NAME(delay_output) (NCURSES_SP_ARGx trailpad / 10);
 #endif /* BSD_TPUTS */
 
     SetOutCh(my_outch);
@@ -425,7 +443,7 @@
 _nc_outc_wrapper(SCREEN *sp, int c)
 {
     if (0 == sp) {
-	return (ERR);
+	return fputc(c, stdout);
     } else {
 	return sp->jump(c);
     }
diff --git a/ncurses/tinfo/lib_ttyflags.c b/ncurses/tinfo/lib_ttyflags.c
index 43bed35..6363a80 100644
--- a/ncurses/tinfo/lib_ttyflags.c
+++ b/ncurses/tinfo/lib_ttyflags.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2014 Free Software Foundation, Inc.              *
+ * Copyright 2020 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            *
@@ -41,42 +42,42 @@
 #define CUR SP_TERMTYPE
 #endif
 
-MODULE_ID("$Id: lib_ttyflags.c,v 1.30 2014/04/26 18:47:20 juergen Exp $")
+MODULE_ID("$Id: lib_ttyflags.c,v 1.36 2020/09/05 22:54:47 tom Exp $")
 
 NCURSES_EXPORT(int)
 NCURSES_SP_NAME(_nc_get_tty_mode) (NCURSES_SP_DCLx TTY * buf)
 {
+    TERMINAL *termp = TerminalOf(SP_PARM);
     int result = OK;
 
-    if (buf == 0 || SP_PARM == 0) {
+    if (buf == 0 || termp == 0) {
 	result = ERR;
     } else {
-	TERMINAL *termp = TerminalOf(SP_PARM);
 
-	if (0 == termp) {
-	    result = ERR;
-	} else {
 #ifdef USE_TERM_DRIVER
+	if (SP_PARM != 0) {
 	    result = CallDriver_2(SP_PARM, td_sgmode, FALSE, buf);
-#else
-	    for (;;) {
-		if (GET_TTY(termp->Filedes, buf) != 0) {
-		    if (errno == EINTR)
-			continue;
-		    result = ERR;
-		}
-		break;
-	    }
-#endif
+	} else {
+	    result = ERR;
 	}
-
-	if (result == ERR)
-	    memset(buf, 0, sizeof(*buf));
+#else
+	for (;;) {
+	    if (GET_TTY(termp->Filedes, buf) != 0) {
+		if (errno == EINTR)
+		    continue;
+		result = ERR;
+	    }
+	    break;
+	}
+#endif
 
 	TR(TRACE_BITS, ("_nc_get_tty_mode(%d): %s",
 			termp ? termp->Filedes : -1,
 			_nc_trace_ttymode(buf)));
     }
+    if (result == ERR && buf != 0)
+	memset(buf, 0, sizeof(*buf));
+
     return (result);
 }
 
@@ -141,7 +142,8 @@
     int rc = ERR;
     TERMINAL *termp = TerminalOf(SP_PARM);
 
-    T((T_CALLED("def_shell_mode(%p)"), (void *) SP_PARM));
+    T((T_CALLED("def_shell_mode(%p) ->term %p"),
+       (void *) SP_PARM, (void *) termp));
 
     if (termp != 0) {
 #ifdef USE_TERM_DRIVER
@@ -154,6 +156,8 @@
 #ifdef TERMIOS
 	    if (termp->Ottyb.c_oflag & OFLAGS_TABS)
 		tab = back_tab = NULL;
+#elif defined(EXP_WIN32_DRIVER)
+	    /* noop */
 #else
 	    if (termp->Ottyb.sg_flags & XTABS)
 		tab = back_tab = NULL;
@@ -179,7 +183,7 @@
     int rc = ERR;
     TERMINAL *termp = TerminalOf(SP_PARM);
 
-    T((T_CALLED("def_prog_mode(%p)"), (void *) SP_PARM));
+    T((T_CALLED("def_prog_mode(%p) ->term %p"), (void *) SP_PARM, (void *) termp));
 
     if (termp != 0) {
 #ifdef USE_TERM_DRIVER
@@ -191,6 +195,8 @@
 	if (_nc_get_tty_mode(&termp->Nttyb) == OK) {
 #ifdef TERMIOS
 	    termp->Nttyb.c_oflag &= (unsigned) (~OFLAGS_TABS);
+#elif defined(EXP_WIN32_DRIVER)
+	    /* noop */
 #else
 	    termp->Nttyb.sg_flags &= (unsigned) (~XTABS);
 #endif
@@ -215,7 +221,7 @@
     int rc = ERR;
     TERMINAL *termp = TerminalOf(SP_PARM);
 
-    T((T_CALLED("reset_prog_mode(%p)"), (void *) SP_PARM));
+    T((T_CALLED("reset_prog_mode(%p) ->term %p"), (void *) SP_PARM, (void *) termp));
 
     if (termp != 0) {
 #ifdef USE_TERM_DRIVER
@@ -247,7 +253,8 @@
     int rc = ERR;
     TERMINAL *termp = TerminalOf(SP_PARM);
 
-    T((T_CALLED("reset_shell_mode(%p)"), (void *) SP_PARM));
+    T((T_CALLED("reset_shell_mode(%p) ->term %p"),
+       (void *) SP_PARM, (void *) termp));
 
     if (termp != 0) {
 #ifdef USE_TERM_DRIVER
diff --git a/ncurses/tinfo/lib_win32con.c b/ncurses/tinfo/lib_win32con.c
new file mode 100644
index 0000000..2d6857a
--- /dev/null
+++ b/ncurses/tinfo/lib_win32con.c
@@ -0,0 +1,1252 @@
+/****************************************************************************
+ * Copyright 2020-2021,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2009,2010 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            *
+ * "Software"), to deal in the Software without restriction, including      *
+ * without limitation the rights to use, copy, modify, merge, publish,      *
+ * distribute, distribute with modifications, sublicense, and/or sell       *
+ * copies of the Software, and to permit persons to whom the Software is    *
+ * furnished to do so, subject to the following conditions:                 *
+ *                                                                          *
+ * The above copyright notice and this permission notice shall be included  *
+ * in all copies or substantial portions of the Software.                   *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
+ * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
+ *                                                                          *
+ * Except as contained in this notice, the name(s) of the above copyright   *
+ * holders shall not be used in advertising or otherwise to promote the     *
+ * sale, use or other dealings in this Software without prior written       *
+ * authorization.                                                           *
+ ****************************************************************************/
+
+/****************************************************************************
+ *  Author: Juergen Pfeifer                                                 *
+ *     and: Thomas E. Dickey                                                *
+ ****************************************************************************/
+
+/*
+ * TODO - GetMousePos(POINT * result) from ntconio.c
+ */
+
+#include <curses.priv.h>
+
+MODULE_ID("$Id: lib_win32con.c,v 1.14 2023/08/05 20:44:38 tom Exp $")
+
+#ifdef _NC_WINDOWS
+
+#ifdef _NC_MINGW
+#include <wchar.h>
+#else
+#include <tchar.h>
+#endif
+
+#include <io.h>
+
+#if USE_WIDEC_SUPPORT
+#define write_screen WriteConsoleOutputW
+#define read_screen  ReadConsoleOutputW
+#else
+#define write_screen WriteConsoleOutput
+#define read_screen  ReadConsoleOutput
+#endif
+
+static bool read_screen_data(void);
+
+#define GenMap(vKey,key) MAKELONG(key, vKey)
+static const LONG keylist[] =
+{
+    GenMap(VK_PRIOR, KEY_PPAGE),
+    GenMap(VK_NEXT, KEY_NPAGE),
+    GenMap(VK_END, KEY_END),
+    GenMap(VK_HOME, KEY_HOME),
+    GenMap(VK_LEFT, KEY_LEFT),
+    GenMap(VK_UP, KEY_UP),
+    GenMap(VK_RIGHT, KEY_RIGHT),
+    GenMap(VK_DOWN, KEY_DOWN),
+    GenMap(VK_DELETE, KEY_DC),
+    GenMap(VK_INSERT, KEY_IC)
+};
+static const LONG ansi_keys[] =
+{
+    GenMap(VK_PRIOR, 'I'),
+    GenMap(VK_NEXT, 'Q'),
+    GenMap(VK_END, 'O'),
+    GenMap(VK_HOME, 'H'),
+    GenMap(VK_LEFT, 'K'),
+    GenMap(VK_UP, 'H'),
+    GenMap(VK_RIGHT, 'M'),
+    GenMap(VK_DOWN, 'P'),
+    GenMap(VK_DELETE, 'S'),
+    GenMap(VK_INSERT, 'R')
+};
+#define array_length(a) (sizeof(a)/sizeof(a[0]))
+#define N_INI ((int)array_length(keylist))
+#define FKEYS 24
+#define MAPSIZE (FKEYS + N_INI)
+
+/*   A process can only have a single console, so it is safe
+     to maintain all the information about it in a single
+     static structure.
+ */
+NCURSES_EXPORT_VAR(ConsoleInfo) _nc_CONSOLE;
+static bool console_initialized = FALSE;
+
+#define EnsureInit() (void)(console_initialized ? TRUE : _nc_console_checkinit(TRUE, TRUE))
+
+#define REQUIRED_MAX_V (DWORD)10
+#define REQUIRED_MIN_V (DWORD)0
+#define REQUIRED_BUILD (DWORD)17763
+/*
+  This function returns 0 if the Windows version has no support for
+  the modern Console interface, otherwise it returns 1
+ */
+NCURSES_EXPORT(int)
+_nc_console_vt_supported(void)
+{
+    OSVERSIONINFO osvi;
+    int res = 0;
+
+    T((T_CALLED("lib_win32con::_nc_console_vt_supported")));
+    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+    GetVersionEx(&osvi);
+    T(("GetVersionEx returnedMajor=%ld, Minor=%ld, Build=%ld",
+       osvi.dwMajorVersion,
+       osvi.dwMinorVersion,
+       osvi.dwBuildNumber));
+    if (osvi.dwMajorVersion >= REQUIRED_MAX_V) {
+	if (osvi.dwMajorVersion == REQUIRED_MAX_V) {
+	    if (((osvi.dwMinorVersion == REQUIRED_MIN_V) &&
+		 (osvi.dwBuildNumber >= REQUIRED_BUILD)) ||
+		((osvi.dwMinorVersion > REQUIRED_MIN_V)))
+		res = 1;
+	} else
+	    res = 1;
+    }
+    returnCode(res);
+}
+
+NCURSES_EXPORT(void)
+_nc_console_size(int *Lines, int *Cols)
+{
+    EnsureInit();
+    if (Lines != NULL && Cols != NULL) {
+	if (WINCONSOLE.buffered) {
+	    *Lines = (int) (WINCONSOLE.SBI.dwSize.Y);
+	    *Cols = (int) (WINCONSOLE.SBI.dwSize.X);
+	} else {
+	    *Lines = (int) (WINCONSOLE.SBI.srWindow.Bottom + 1 -
+			    WINCONSOLE.SBI.srWindow.Top);
+	    *Cols = (int) (WINCONSOLE.SBI.srWindow.Right + 1 -
+			   WINCONSOLE.SBI.srWindow.Left);
+	}
+    }
+}
+
+/* Convert a file descriptor into a HANDLE
+   That's not necessarily a console HANDLE
+*/
+NCURSES_EXPORT(HANDLE)
+_nc_console_handle(int fd)
+{
+    intptr_t value = _get_osfhandle(fd);
+    return (HANDLE) value;
+}
+
+/* Validate that a HANDLE is actually a
+   console HANDLE
+*/
+static BOOL
+IsConsoleHandle(HANDLE hdl)
+{
+    DWORD dwFlag = 0;
+    BOOL result = FALSE;
+
+    T((T_CALLED("lib_win32con::IsConsoleHandle(HANDLE=%p"), hdl));
+
+    EnsureInit();
+
+    if (!GetConsoleMode(hdl, &dwFlag)) {
+	T(("GetConsoleMode failed"));
+    } else {
+	result = TRUE;
+    }
+
+    returnBool(result);
+}
+
+/*   This is used when running in terminfo mode to discover,
+     whether or not the "terminal" is actually a Windows
+     Console. It is the responsibility of the console to deal
+     with the terminal escape sequences that are sent by
+     terminfo.
+ */
+NCURSES_EXPORT(int)
+_nc_console_test(int fd)
+{
+    int code = 0;
+    HANDLE hdl = INVALID_HANDLE_VALUE;
+    T((T_CALLED("lib_win32con::_nc_console_test(%d)"), fd));
+    hdl = _nc_console_handle(fd);
+    code = (int) IsConsoleHandle(hdl);
+    returnCode(code);
+}
+
+#define OutHandle() ((WINCONSOLE.isTermInfoConsole || WINCONSOLE.progMode) ? WINCONSOLE.hdl : WINCONSOLE.out)
+
+NCURSES_EXPORT(void)
+_nc_console_selectActiveHandle(void)
+{
+    if (WINCONSOLE.lastOut != WINCONSOLE.hdl) {
+	WINCONSOLE.lastOut = WINCONSOLE.hdl;
+	SetConsoleActiveScreenBuffer(WINCONSOLE.lastOut);
+    }
+}
+
+NCURSES_EXPORT(HANDLE)
+_nc_console_fd2handle(int fd)
+{
+    HANDLE hdl = _nc_console_handle(fd);
+    if (hdl == WINCONSOLE.inp) {
+	T(("lib_win32con:validateHandle %d -> WINCONSOLE.inp", fd));
+    } else if (hdl == WINCONSOLE.hdl) {
+	T(("lib_win32con:validateHandle %d -> WINCONSOLE.hdl", fd));
+    } else if (hdl == WINCONSOLE.out) {
+	T(("lib_win32con:validateHandle %d -> WINCONSOLE.out", fd));
+    } else {
+	T(("lib_win32con:validateHandle %d maps to unknown HANDLE", fd));
+	hdl = INVALID_HANDLE_VALUE;
+    }
+#if 1
+    assert(hdl != INVALID_HANDLE_VALUE);
+#endif
+    if (hdl != INVALID_HANDLE_VALUE) {
+	if (hdl != WINCONSOLE.inp && (!WINCONSOLE.isTermInfoConsole && WINCONSOLE.progMode)) {
+	    if (hdl == WINCONSOLE.out && hdl != WINCONSOLE.hdl) {
+		T(("lib_win32con:validateHandle forcing WINCONSOLE.out -> WINCONSOLE.hdl"));
+		hdl = WINCONSOLE.hdl;
+	    }
+	}
+    }
+    return hdl;
+}
+
+NCURSES_EXPORT(int)
+_nc_console_setmode(HANDLE hdl, const TTY * arg)
+{
+    DWORD dwFlag = 0;
+    int code = ERR;
+    HANDLE alt;
+
+    if (arg) {
+#ifdef TRACE
+	TTY TRCTTY;
+#define TRCTTYOUT(flag) TRCTTY.dwFlagOut = flag
+#define TRCTTYIN(flag)  TRCTTY.dwFlagIn = flag
+#else
+#define TRCTTYOUT(flag)
+#define TRCTTYIN(flag)
+#endif
+	T(("lib_win32con:_nc_console_setmode %s", _nc_trace_ttymode(arg)));
+	if (hdl == WINCONSOLE.inp) {
+	    dwFlag = arg->dwFlagIn | ENABLE_MOUSE_INPUT | VT_FLAG_IN;
+	    if (WINCONSOLE.isTermInfoConsole)
+		dwFlag |= (VT_FLAG_IN);
+	    else
+		dwFlag &= (DWORD) ~ (VT_FLAG_IN);
+	    TRCTTYIN(dwFlag);
+	    SetConsoleMode(hdl, dwFlag);
+
+	    alt = OutHandle();
+	    dwFlag = arg->dwFlagOut;
+	    if (WINCONSOLE.isTermInfoConsole)
+		dwFlag |= (VT_FLAG_OUT);
+	    else
+		dwFlag |= (VT_FLAG_OUT);
+	    TRCTTYOUT(dwFlag);
+	    SetConsoleMode(alt, dwFlag);
+	} else {
+	    dwFlag = arg->dwFlagOut;
+	    if (WINCONSOLE.isTermInfoConsole)
+		dwFlag |= (VT_FLAG_OUT);
+	    else
+		dwFlag |= (VT_FLAG_OUT);
+	    TRCTTYOUT(dwFlag);
+	    SetConsoleMode(hdl, dwFlag);
+
+	    alt = WINCONSOLE.inp;
+	    dwFlag = arg->dwFlagIn | ENABLE_MOUSE_INPUT;
+	    if (WINCONSOLE.isTermInfoConsole)
+		dwFlag |= (VT_FLAG_IN);
+	    else
+		dwFlag &= (DWORD) ~ (VT_FLAG_IN);
+	    TRCTTYIN(dwFlag);
+	    SetConsoleMode(alt, dwFlag);
+	    T(("effective mode set %s", _nc_trace_ttymode(&TRCTTY)));
+	}
+	code = OK;
+    }
+    return (code);
+}
+
+NCURSES_EXPORT(int)
+_nc_console_getmode(HANDLE hdl, TTY * arg)
+{
+    int code = ERR;
+
+    if (arg) {
+	DWORD dwFlag = 0;
+	HANDLE alt;
+
+	if (hdl == WINCONSOLE.inp) {
+	    if (GetConsoleMode(hdl, &dwFlag)) {
+		arg->dwFlagIn = dwFlag;
+		alt = OutHandle();
+		if (GetConsoleMode(alt, &dwFlag)) {
+		    arg->dwFlagOut = dwFlag;
+		    code = OK;
+		}
+	    }
+	} else {
+	    if (GetConsoleMode(hdl, &dwFlag)) {
+		arg->dwFlagOut = dwFlag;
+		alt = WINCONSOLE.inp;
+		if (GetConsoleMode(alt, &dwFlag)) {
+		    arg->dwFlagIn = dwFlag;
+		    code = OK;
+		}
+	    }
+	}
+    }
+    T(("lib_win32con:_nc_console_getmode %s", _nc_trace_ttymode(arg)));
+    return (code);
+}
+
+NCURSES_EXPORT(int)
+_nc_console_flush(HANDLE hdl)
+{
+    int code = OK;
+
+    T((T_CALLED("lib_win32con::_nc_console_flush(hdl=%p"), hdl));
+
+    if (hdl != INVALID_HANDLE_VALUE) {
+	if (hdl == WINCONSOLE.hdl ||
+	    hdl == WINCONSOLE.inp ||
+	    hdl == WINCONSOLE.out) {
+	    if (!FlushConsoleInputBuffer(WINCONSOLE.inp))
+		code = ERR;
+	} else {
+	    code = ERR;
+	    T(("_nc_console_flush not requesting a handle owned by console."));
+	}
+    }
+    returnCode(code);
+}
+
+NCURSES_EXPORT(WORD)
+_nc_console_MapColor(bool fore, int color)
+{
+    static const int _cmap[] =
+    {0, 4, 2, 6, 1, 5, 3, 7};
+    int a;
+    if (color < 0 || color > 7)
+	a = fore ? 7 : 0;
+    else
+	a = _cmap[color];
+    if (!fore)
+	a = a << 4;
+    return (WORD) a;
+}
+
+/*
+ * Attempt to save the screen contents.  PDCurses does this if
+ * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
+ * restoration as if the library had allocated a console buffer.  MSDN
+ * says that the data which can be read is limited to 64Kb (and may be
+ * less).
+ */
+static bool
+save_original_screen(void)
+{
+    bool result = FALSE;
+
+    WINCONSOLE.save_region.Top = 0;
+    WINCONSOLE.save_region.Left = 0;
+    WINCONSOLE.save_region.Bottom = (SHORT) (WINCONSOLE.SBI.dwSize.Y - 1);
+    WINCONSOLE.save_region.Right = (SHORT) (WINCONSOLE.SBI.dwSize.X - 1);
+
+    if (read_screen_data()) {
+	result = TRUE;
+    } else {
+
+	WINCONSOLE.save_region.Top = WINCONSOLE.SBI.srWindow.Top;
+	WINCONSOLE.save_region.Left = WINCONSOLE.SBI.srWindow.Left;
+	WINCONSOLE.save_region.Bottom = WINCONSOLE.SBI.srWindow.Bottom;
+	WINCONSOLE.save_region.Right = WINCONSOLE.SBI.srWindow.Right;
+
+	WINCONSOLE.window_only = TRUE;
+
+	if (read_screen_data()) {
+	    result = TRUE;
+	}
+    }
+
+    T(("... save original screen contents %s", result ? "ok" : "err"));
+    return result;
+}
+
+#if 0
+static bool
+restore_original_screen(void)
+{
+    COORD bufferCoord;
+    bool result = FALSE;
+    SMALL_RECT save_region = WINCONSOLE.save_region;
+
+    T(("... restoring %s",
+       WINCONSOLE.window_only ? "window" : "entire buffer"));
+
+    bufferCoord.X = (SHORT) (WINCONSOLE.window_only ?
+			     WINCONSOLE.SBI.srWindow.Left : 0);
+    bufferCoord.Y = (SHORT) (WINCONSOLE.window_only ?
+			     WINCONSOLE.SBI.srWindow.Top : 0);
+
+    if (write_screen(WINCONSOLE.hdl,
+		     WINCONSOLE.save_screen,
+		     WINCONSOLE.save_size,
+		     bufferCoord,
+		     &save_region)) {
+	result = TRUE;
+	mvcur(-1, -1, LINES - 2, 0);
+	T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
+	   WINCONSOLE.save_size.Y,
+	   WINCONSOLE.save_size.X,
+	   save_region.Top,
+	   save_region.Left,
+	   save_region.Bottom,
+	   save_region.Right));
+    } else {
+	T(("... restore original screen contents err"));
+    }
+    return result;
+}
+#endif
+
+static bool
+read_screen_data(void)
+{
+    bool result = FALSE;
+    COORD bufferCoord;
+    size_t want;
+
+    WINCONSOLE.save_size.X = (SHORT) (WINCONSOLE.save_region.Right
+				      - WINCONSOLE.save_region.Left + 1);
+    WINCONSOLE.save_size.Y = (SHORT) (WINCONSOLE.save_region.Bottom
+				      - WINCONSOLE.save_region.Top + 1);
+
+    want = (size_t) (WINCONSOLE.save_size.X * WINCONSOLE.save_size.Y);
+
+    if ((WINCONSOLE.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
+	bufferCoord.X = (SHORT) (WINCONSOLE.window_only ?
+				 WINCONSOLE.SBI.srWindow.Left : 0);
+	bufferCoord.Y = (SHORT) (WINCONSOLE.window_only ?
+				 WINCONSOLE.SBI.srWindow.Top : 0);
+
+	T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
+	   WINCONSOLE.window_only ? "window" : "buffer",
+	   WINCONSOLE.save_size.Y, WINCONSOLE.save_size.X,
+	   WINCONSOLE.save_region.Top,
+	   WINCONSOLE.save_region.Left,
+	   WINCONSOLE.save_region.Bottom,
+	   WINCONSOLE.save_region.Right,
+	   bufferCoord.Y,
+	   bufferCoord.X));
+
+	if (read_screen(WINCONSOLE.hdl,
+			WINCONSOLE.save_screen,
+			WINCONSOLE.save_size,
+			bufferCoord,
+			&WINCONSOLE.save_region)) {
+	    result = TRUE;
+	} else {
+	    T((" error %#lx", (unsigned long) GetLastError()));
+	    FreeAndNull(WINCONSOLE.save_screen);
+	}
+    }
+
+    return result;
+}
+
+NCURSES_EXPORT(bool)
+_nc_console_get_SBI(void)
+{
+    bool rc = FALSE;
+    if (GetConsoleScreenBufferInfo(WINCONSOLE.hdl, &(WINCONSOLE.SBI))) {
+	T(("GetConsoleScreenBufferInfo"));
+	T(("... buffer(X:%d Y:%d)",
+	   WINCONSOLE.SBI.dwSize.X,
+	   WINCONSOLE.SBI.dwSize.Y));
+	T(("... window(X:%d Y:%d)",
+	   WINCONSOLE.SBI.dwMaximumWindowSize.X,
+	   WINCONSOLE.SBI.dwMaximumWindowSize.Y));
+	T(("... cursor(X:%d Y:%d)",
+	   WINCONSOLE.SBI.dwCursorPosition.X,
+	   WINCONSOLE.SBI.dwCursorPosition.Y));
+	T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
+	   WINCONSOLE.SBI.srWindow.Top,
+	   WINCONSOLE.SBI.srWindow.Bottom,
+	   WINCONSOLE.SBI.srWindow.Left,
+	   WINCONSOLE.SBI.srWindow.Right));
+	if (WINCONSOLE.buffered) {
+	    WINCONSOLE.origin.X = 0;
+	    WINCONSOLE.origin.Y = 0;
+	} else {
+	    WINCONSOLE.origin.X = WINCONSOLE.SBI.srWindow.Left;
+	    WINCONSOLE.origin.Y = WINCONSOLE.SBI.srWindow.Top;
+	}
+	rc = TRUE;
+    } else {
+	T(("GetConsoleScreenBufferInfo ERR"));
+    }
+    return rc;
+}
+
+#define MIN_WIDE 80
+#define MIN_HIGH 24
+
+/*
+ * In "normal" mode, reset the buffer- and window-sizes back to their original values.
+ */
+NCURSES_EXPORT(void)
+_nc_console_set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info)
+{
+    SMALL_RECT rect;
+    COORD coord;
+    bool changed = FALSE;
+
+    T((T_CALLED("lib_win32con::_nc_console_set_scrollback(%s)"),
+       (normal
+	? "normal"
+	: "application")));
+
+    T(("... SBI.srWindow %d,%d .. %d,%d",
+       info->srWindow.Top,
+       info->srWindow.Left,
+       info->srWindow.Bottom,
+       info->srWindow.Right));
+    T(("... SBI.dwSize %dx%d",
+       info->dwSize.Y,
+       info->dwSize.X));
+
+    if (normal) {
+	rect = info->srWindow;
+	coord = info->dwSize;
+	if (memcmp(info, &WINCONSOLE.SBI, sizeof(*info)) != 0) {
+	    changed = TRUE;
+	    WINCONSOLE.SBI = *info;
+	}
+    } else {
+	int high = info->srWindow.Bottom - info->srWindow.Top + 1;
+	int wide = info->srWindow.Right - info->srWindow.Left + 1;
+
+	if (high < MIN_HIGH) {
+	    T(("... height %d < %d", high, MIN_HIGH));
+	    high = MIN_HIGH;
+	    changed = TRUE;
+	}
+	if (wide < MIN_WIDE) {
+	    T(("... width %d < %d", wide, MIN_WIDE));
+	    wide = MIN_WIDE;
+	    changed = TRUE;
+	}
+
+	rect.Left =
+	    rect.Top = 0;
+	rect.Right = (SHORT) (wide - 1);
+	rect.Bottom = (SHORT) (high - 1);
+
+	coord.X = (SHORT) wide;
+	coord.Y = (SHORT) high;
+
+	if (info->dwSize.Y != high ||
+	    info->dwSize.X != wide ||
+	    info->srWindow.Top != 0 ||
+	    info->srWindow.Left != 0) {
+	    changed = TRUE;
+	}
+
+    }
+
+    if (changed) {
+	T(("... coord %d,%d", coord.Y, coord.X));
+	T(("... rect %d,%d - %d,%d",
+	   rect.Top, rect.Left,
+	   rect.Bottom, rect.Right));
+	SetConsoleScreenBufferSize(WINCONSOLE.hdl, coord);	/* dwSize */
+	SetConsoleWindowInfo(WINCONSOLE.hdl, TRUE, &rect);	/* srWindow */
+	_nc_console_get_SBI();
+    }
+    returnVoid;
+}
+
+static ULONGLONG
+tdiff(FILETIME fstart, FILETIME fend)
+{
+    ULARGE_INTEGER ustart;
+    ULARGE_INTEGER uend;
+    ULONGLONG diff;
+
+    ustart.LowPart = fstart.dwLowDateTime;
+    ustart.HighPart = fstart.dwHighDateTime;
+    uend.LowPart = fend.dwLowDateTime;
+    uend.HighPart = fend.dwHighDateTime;
+
+    diff = (uend.QuadPart - ustart.QuadPart) / 10000;
+    return diff;
+}
+
+static int
+Adjust(int milliseconds, int diff)
+{
+    if (milliseconds != INFINITY) {
+	milliseconds -= diff;
+	if (milliseconds < 0)
+	    milliseconds = 0;
+    }
+    return milliseconds;
+}
+
+#define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
+                     FROM_LEFT_2ND_BUTTON_PRESSED | \
+                     FROM_LEFT_3RD_BUTTON_PRESSED | \
+                     FROM_LEFT_4TH_BUTTON_PRESSED | \
+                     RIGHTMOST_BUTTON_PRESSED)
+
+static mmask_t
+decode_mouse(SCREEN *sp, int mask)
+{
+    mmask_t result = 0;
+
+    (void) sp;
+    assert(sp && console_initialized);
+
+    if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
+	result |= BUTTON1_PRESSED;
+    if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
+	result |= BUTTON2_PRESSED;
+    if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
+	result |= BUTTON3_PRESSED;
+    if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
+	result |= BUTTON4_PRESSED;
+
+    if (mask & RIGHTMOST_BUTTON_PRESSED) {
+	switch (WINCONSOLE.numButtons) {
+	case 1:
+	    result |= BUTTON1_PRESSED;
+	    break;
+	case 2:
+	    result |= BUTTON2_PRESSED;
+	    break;
+	case 3:
+	    result |= BUTTON3_PRESSED;
+	    break;
+	case 4:
+	    result |= BUTTON4_PRESSED;
+	    break;
+	}
+    }
+
+    return result;
+}
+
+#define AdjustY() (WINCONSOLE.buffered ? 0 : (int) WINCONSOLE.SBI.srWindow.Top)
+
+static bool
+handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
+{
+    MEVENT work;
+    bool result = FALSE;
+
+    assert(sp);
+
+    sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
+    sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
+
+    /*
+     * We're only interested if the button is pressed or released.
+     * FIXME: implement continuous event-tracking.
+     */
+    if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
+	memset(&work, 0, sizeof(work));
+
+	if (sp->_drv_mouse_new_buttons) {
+	    work.bstate |= decode_mouse(sp, sp->_drv_mouse_new_buttons);
+	} else {
+	    /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
+	    work.bstate |= (decode_mouse(sp, sp->_drv_mouse_old_buttons)
+			    >> 1);
+	    result = TRUE;
+	}
+
+	work.x = mer.dwMousePosition.X;
+	work.y = mer.dwMousePosition.Y - AdjustY();
+
+	sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
+	sp->_drv_mouse_tail += 1;
+    }
+    return result;
+}
+
+static int
+rkeycompare(const void *el1, const void *el2)
+{
+    WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
+    WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
+
+    return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
+}
+
+static int
+keycompare(const void *el1, const void *el2)
+{
+    WORD key1 = HIWORD((*((const LONG *) el1)));
+    WORD key2 = HIWORD((*((const LONG *) el2)));
+
+    return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
+}
+
+static int
+MapKey(WORD vKey)
+{
+    int code = -1;
+
+    if (!WINCONSOLE.isTermInfoConsole) {
+	WORD nKey = 0;
+	void *res;
+	LONG key = GenMap(vKey, 0);
+
+	res = bsearch(&key,
+		      WINCONSOLE.map,
+		      (size_t) (N_INI + FKEYS),
+		      sizeof(keylist[0]),
+		      keycompare);
+	if (res) {
+	    key = *((LONG *) res);
+	    nKey = LOWORD(key);
+	    code = (int) (nKey & 0x7fff);
+	    if (nKey & 0x8000)
+		code = -code;
+	}
+    }
+    return code;
+}
+
+static int
+AnsiKey(WORD vKey)
+{
+    int code = -1;
+
+    if (!WINCONSOLE.isTermInfoConsole) {
+	WORD nKey = 0;
+	void *res;
+	LONG key = GenMap(vKey, 0);
+
+	res = bsearch(&key,
+		      WINCONSOLE.ansi_map,
+		      (size_t) (N_INI + FKEYS),
+		      sizeof(keylist[0]),
+		      keycompare);
+	if (res) {
+	    key = *((LONG *) res);
+	    nKey = LOWORD(key);
+	    code = (int) (nKey & 0x7fff);
+	    if (nKey & 0x8000)
+		code = -code;
+	}
+    }
+    return code;
+}
+
+NCURSES_EXPORT(int)
+_nc_console_keyok(int keycode, int flag)
+{
+    int code = ERR;
+    WORD nKey;
+    WORD vKey;
+    void *res;
+    LONG key = GenMap(0, (WORD) keycode);
+
+    T((T_CALLED("lib_win32con::_nc_console_keyok(%d, %d)"), keycode, flag));
+
+    res = bsearch(&key,
+		  WINCONSOLE.rmap,
+		  (size_t) (N_INI + FKEYS),
+		  sizeof(keylist[0]),
+		  rkeycompare);
+    if (res) {
+	key = *((LONG *) res);
+	vKey = HIWORD(key);
+	nKey = (LOWORD(key)) & 0x7fff;
+	if (!flag)
+	    nKey |= 0x8000;
+	*(LONG *) res = GenMap(vKey, nKey);
+    }
+    returnCode(code);
+}
+
+NCURSES_EXPORT(bool)
+_nc_console_keyExist(int keycode)
+{
+    WORD nKey;
+    void *res;
+    bool found = FALSE;
+    LONG key = GenMap(0, (WORD) keycode);
+
+    T((T_CALLED("lib_win32con::_nc_console_keyExist(%d)"), keycode));
+    res = bsearch(&key,
+		  WINCONSOLE.rmap,
+		  (size_t) (N_INI + FKEYS),
+		  sizeof(keylist[0]),
+		  rkeycompare);
+    if (res) {
+	key = *((LONG *) res);
+	nKey = LOWORD(key);
+	if (!(nKey & 0x8000))
+	    found = TRUE;
+    }
+    returnCode(found);
+}
+
+NCURSES_EXPORT(int)
+_nc_console_twait(
+		     SCREEN *sp,
+		     HANDLE hdl,
+		     int mode,
+		     int milliseconds,
+		     int *timeleft
+		     EVENTLIST_2nd(_nc_eventlist * evl))
+{
+    INPUT_RECORD inp_rec;
+    BOOL b;
+    DWORD nRead = 0, rc = (DWORD) (-1);
+    int code = 0;
+    FILETIME fstart;
+    FILETIME fend;
+    int diff;
+    bool isNoDelay = (milliseconds == 0);
+
+#ifdef NCURSES_WGETCH_EVENTS
+    (void) evl;			/* TODO: implement wgetch-events */
+#endif
+
+#define IGNORE_CTRL_KEYS (SHIFT_PRESSED|LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED| \
+                          LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)
+#define CONSUME() ReadConsoleInput(hdl, &inp_rec, 1, &nRead)
+
+    assert(sp);
+
+    TR(TRACE_IEVENT, ("start twait: hdl=%p, %d milliseconds, mode: %d",
+		      hdl, milliseconds, mode));
+
+    if (milliseconds < 0)
+	milliseconds = INFINITY;
+
+    memset(&inp_rec, 0, sizeof(inp_rec));
+
+    while (true) {
+	if (!isNoDelay) {
+	    GetSystemTimeAsFileTime(&fstart);
+	    rc = WaitForSingleObject(hdl, (DWORD) milliseconds);
+	    GetSystemTimeAsFileTime(&fend);
+	    diff = (int) tdiff(fstart, fend);
+	    milliseconds = Adjust(milliseconds, diff);
+	    if (milliseconds < 0)
+		break;
+	}
+
+	if (isNoDelay || (rc == WAIT_OBJECT_0)) {
+	    if (mode) {
+		nRead = 0;
+		b = GetNumberOfConsoleInputEvents(hdl, &nRead);
+		if (!b) {
+		    T(("twait:err GetNumberOfConsoleInputEvents"));
+		}
+		if (isNoDelay && b) {
+		    T(("twait: Events Available: %ld", nRead));
+		    if (nRead == 0) {
+			code = 0;
+			goto end;
+		    } else {
+			DWORD n = 0;
+			INPUT_RECORD *pInpRec =
+			TypeAlloca(INPUT_RECORD, nRead);
+			if (pInpRec != NULL) {
+			    DWORD i;
+			    BOOL f;
+			    memset(pInpRec, 0, sizeof(INPUT_RECORD) * nRead);
+			    f = PeekConsoleInput(hdl, pInpRec, nRead, &n);
+			    if (f) {
+				for (i = 0; i < n; i++) {
+				    if (pInpRec[i].EventType == KEY_EVENT) {
+					if (pInpRec[i].Event.KeyEvent.bKeyDown) {
+					    DWORD ctrlMask =
+					    (pInpRec[i].Event.KeyEvent.dwControlKeyState &
+					     IGNORE_CTRL_KEYS);
+					    if (!ctrlMask) {
+						code = TW_INPUT;
+						goto end;
+					    }
+					}
+				    }
+				}
+			    } else {
+				T(("twait:err PeekConsoleInput"));
+			    }
+			    code = 0;
+			    goto end;
+			} else {
+			    T(("twait:err could not alloca input records"));
+			}
+		    }
+		}
+		if (b && nRead > 0) {
+		    b = PeekConsoleInput(hdl, &inp_rec, 1, &nRead);
+		    if (!b) {
+			T(("twait:err PeekConsoleInput"));
+		    }
+		    if (b && nRead > 0) {
+			switch (inp_rec.EventType) {
+			case KEY_EVENT:
+			    if (mode & TW_INPUT) {
+				WORD vk =
+				inp_rec.Event.KeyEvent.wVirtualKeyCode;
+				char ch =
+				inp_rec.Event.KeyEvent.uChar.AsciiChar;
+				T(("twait:event KEY_EVENT"));
+				T(("twait vk=%d, ch=%d, keydown=%d",
+				   vk, ch, inp_rec.Event.KeyEvent.bKeyDown));
+				if (inp_rec.Event.KeyEvent.bKeyDown) {
+				    T(("twait:event KeyDown"));
+				    if (!WINCONSOLE.isTermInfoConsole &&
+					(0 == ch)) {
+					int nKey = MapKey(vk);
+					if (nKey < 0) {
+					    CONSUME();
+					    continue;
+					}
+				    }
+				    code = TW_INPUT;
+				    goto end;
+				} else {
+				    CONSUME();
+				}
+			    }
+			    continue;
+			case MOUSE_EVENT:
+			    T(("twait:event MOUSE_EVENT"));
+			    if (decode_mouse(sp,
+					     (inp_rec.Event.MouseEvent.dwButtonState
+					      & BUTTON_MASK)) == 0) {
+				CONSUME();
+			    } else if (mode & TW_MOUSE) {
+				code = TW_MOUSE;
+				goto end;
+			    }
+			    continue;
+			    /* e.g., FOCUS_EVENT */
+			default:
+			    T(("twait:event Tyoe %d", inp_rec.EventType));
+			    CONSUME();
+			    _nc_console_selectActiveHandle();
+			    continue;
+			}
+		    }
+		}
+	    }
+	    continue;
+	} else {
+	    if (rc != WAIT_TIMEOUT) {
+		code = -1;
+		break;
+	    } else {
+		code = 0;
+		break;
+	    }
+	}
+    }
+  end:
+
+    TR(TRACE_IEVENT, ("end twait: returned %d (%lu), remaining time %d msec",
+		      code, GetLastError(), milliseconds));
+
+    if (timeleft)
+	*timeleft = milliseconds;
+
+    return code;
+}
+
+NCURSES_EXPORT(int)
+_nc_console_testmouse(
+			 SCREEN *sp,
+			 HANDLE hdl,
+			 int delay
+			 EVENTLIST_2nd(_nc_eventlist * evl))
+{
+    int rc = 0;
+
+    assert(sp);
+
+    if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
+	rc = TW_MOUSE;
+    } else {
+	rc = _nc_console_twait(sp,
+			       hdl,
+			       TWAIT_MASK,
+			       delay,
+			       (int *) 0
+			       EVENTLIST_2nd(evl));
+    }
+    return rc;
+}
+
+NCURSES_EXPORT(int)
+_nc_console_read(
+		    SCREEN *sp,
+		    HANDLE hdl,
+		    int *buf)
+{
+    int rc = -1;
+    INPUT_RECORD inp_rec;
+    BOOL b;
+    DWORD nRead;
+    WORD vk;
+
+    assert(sp);
+    assert(buf);
+
+    memset(&inp_rec, 0, sizeof(inp_rec));
+
+    T((T_CALLED("lib_win32con::_nc_console_read(%p)"), sp));
+
+    while ((b = ReadConsoleInput(hdl, &inp_rec, 1, &nRead))) {
+	if (b && nRead > 0) {
+	    if (rc < 0)
+		rc = 0;
+	    rc = rc + (int) nRead;
+	    if (inp_rec.EventType == KEY_EVENT) {
+		if (!inp_rec.Event.KeyEvent.bKeyDown)
+		    continue;
+		*buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
+		vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
+		/*
+		 * There are 24 virtual function-keys, and typically
+		 * 12 function-keys on a keyboard.  Use the shift-modifier
+		 * to provide the remaining 12 keys.
+		 */
+		if (vk >= VK_F1 && vk <= VK_F12) {
+		    if (inp_rec.Event.KeyEvent.dwControlKeyState &
+			SHIFT_PRESSED) {
+			vk = (WORD) (vk + 12);
+		    }
+		}
+		if (*buf == 0) {
+		    int key = MapKey(vk);
+		    if (key < 0)
+			continue;
+		    if (sp->_keypad_on) {
+			*buf = key;
+		    } else {
+			ungetch('\0');
+			*buf = AnsiKey(vk);
+		    }
+		}
+		break;
+	    } else if (inp_rec.EventType == MOUSE_EVENT) {
+		if (handle_mouse(sp,
+				 inp_rec.Event.MouseEvent)) {
+		    *buf = KEY_MOUSE;
+		    break;
+		}
+	    }
+	    continue;
+	}
+    }
+    returnCode(rc);
+}
+
+/*   Our replacement for the systems _isatty to include also
+     a test for mintty. This is called from the NC_ISATTY macro
+     defined in curses.priv.h
+
+     Return codes:
+     - 0 : Not a TTY
+     - 1 : A Windows character device detected by _isatty
+     - 2 : A future implementation may return 2 for mintty
+ */
+NCURSES_EXPORT(int)
+_nc_console_isatty(int fd)
+{
+    int result = 0;
+    T((T_CALLED("lib_win32con::_nc_console_isatty(%d"), fd));
+
+    if (_isatty(fd))
+	result = 1;
+#ifdef _NC_CHECK_MINTTY
+    else {
+	if (_nc_console_checkmintty(fd, NULL)) {
+	    result = 2;
+	    fprintf(stderr,
+		    "ncurses on Windows must run in a Windows console.\n"
+		    "On newer versions of Windows, the calling program should create a PTY-like.\n"
+		    "device using the CreatePseudoConsole Windows API call.\n");
+	    exit(EXIT_FAILURE);
+	}
+    }
+#endif
+    returnCode(result);
+}
+
+NCURSES_EXPORT(bool)
+_nc_console_checkinit(bool initFlag, bool assumeTermInfo)
+{
+    bool res = FALSE;
+
+    T((T_CALLED("lib_win32con::_nc_console_checkinit(initFlag=%d, assumeTermInfo=%d)"),
+       initFlag, assumeTermInfo));
+
+    if (!initFlag) {
+	res = console_initialized;
+    } else {
+	/* initialize once, or not at all */
+	if (!console_initialized) {
+	    int i;
+	    DWORD num_buttons;
+	    WORD a;
+	    BOOL buffered = FALSE;
+	    BOOL b;
+
+	    START_TRACE();
+	    WINCONSOLE.isTermInfoConsole = assumeTermInfo;
+
+	    WINCONSOLE.map = (LPDWORD) malloc(sizeof(DWORD) * MAPSIZE);
+	    WINCONSOLE.rmap = (LPDWORD) malloc(sizeof(DWORD) * MAPSIZE);
+	    WINCONSOLE.ansi_map = (LPDWORD) malloc(sizeof(DWORD) * MAPSIZE);
+
+	    for (i = 0; i < (N_INI + FKEYS); i++) {
+		if (i < N_INI) {
+		    WINCONSOLE.rmap[i] = WINCONSOLE.map[i] =
+			(DWORD) keylist[i];
+		    WINCONSOLE.ansi_map[i] = (DWORD) ansi_keys[i];
+		} else {
+		    WINCONSOLE.rmap[i] = WINCONSOLE.map[i] =
+			(DWORD) GenMap((VK_F1 + (i - N_INI)),
+				       (KEY_F(1) + (i - N_INI)));
+		    WINCONSOLE.ansi_map[i] =
+			(DWORD) GenMap((VK_F1 + (i - N_INI)),
+				       (';' + (i - N_INI)));
+		}
+	    }
+	    qsort(WINCONSOLE.ansi_map,
+		  (size_t) (MAPSIZE),
+		  sizeof(keylist[0]),
+		  keycompare);
+	    qsort(WINCONSOLE.map,
+		  (size_t) (MAPSIZE),
+		  sizeof(keylist[0]),
+		  keycompare);
+	    qsort(WINCONSOLE.rmap,
+		  (size_t) (MAPSIZE),
+		  sizeof(keylist[0]),
+		  rkeycompare);
+
+	    if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
+		WINCONSOLE.numButtons = (int) num_buttons;
+	    } else {
+		WINCONSOLE.numButtons = 1;
+	    }
+
+	    a = _nc_console_MapColor(true, COLOR_WHITE) |
+		_nc_console_MapColor(false, COLOR_BLACK);
+	    for (i = 0; i < CON_NUMPAIRS; i++)
+		WINCONSOLE.pairs[i] = a;
+
+#define SaveConsoleMode(handle, value) \
+            GetConsoleMode(WINCONSOLE.handle, &WINCONSOLE.originalMode.value)
+
+	    if (WINCONSOLE.isTermInfoConsole) {
+		WINCONSOLE.inp = GetStdHandle(STD_INPUT_HANDLE);
+		WINCONSOLE.out = GetStdHandle(STD_OUTPUT_HANDLE);
+		WINCONSOLE.hdl = WINCONSOLE.out;
+
+		SaveConsoleMode(inp, dwFlagIn);
+		SaveConsoleMode(out, dwFlagOut);
+
+	    } else {
+		b = AllocConsole();
+
+		if (!b)
+		    b = AttachConsole(ATTACH_PARENT_PROCESS);
+
+		WINCONSOLE.inp = GetDirectHandle("CONIN$", FILE_SHARE_READ);
+		WINCONSOLE.out = GetDirectHandle("CONOUT$", FILE_SHARE_WRITE);
+
+		SaveConsoleMode(inp, dwFlagIn);
+		SaveConsoleMode(out, dwFlagOut);
+
+		if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
+		    WINCONSOLE.hdl = WINCONSOLE.out;
+		    T(("... will not buffer console"));
+		} else {
+		    T(("... creating console buffer"));
+		    WINCONSOLE.hdl =
+			CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+						  FILE_SHARE_READ | FILE_SHARE_WRITE,
+						  NULL,
+						  CONSOLE_TEXTMODE_BUFFER,
+						  NULL);
+		    buffered = TRUE;
+		}
+	    }
+
+	    /* We set binary I/O even when using the console
+	       driver to cover the situation, that the
+	       TERM variable is set to #win32con, but actually
+	       Windows supports virtual terminal processing.
+	       So if terminfo functions are used in this setup,
+	       they actually may work.
+	     */
+	    _setmode(fileno(stdin), _O_BINARY);
+	    _setmode(fileno(stdout), _O_BINARY);
+
+	    if (WINCONSOLE.hdl != INVALID_HANDLE_VALUE) {
+		WINCONSOLE.buffered = buffered;
+		_nc_console_get_SBI();
+		WINCONSOLE.save_SBI = WINCONSOLE.SBI;
+		if (!buffered) {
+		    save_original_screen();
+		    _nc_console_set_scrollback(FALSE, &WINCONSOLE.SBI);
+		}
+		GetConsoleCursorInfo(WINCONSOLE.hdl, &WINCONSOLE.save_CI);
+		T(("... initial cursor is %svisible, %d%%",
+		   (WINCONSOLE.save_CI.bVisible ? "" : "not-"),
+		   (int) WINCONSOLE.save_CI.dwSize));
+	    }
+
+	    WINCONSOLE.initialized = TRUE;
+	    console_initialized = TRUE;
+	}
+	res = (WINCONSOLE.hdl != INVALID_HANDLE_VALUE);
+    }
+    returnBool(res);
+}
+
+#endif // _NC_WINDOWS
diff --git a/ncurses/tinfo/lib_win32util.c b/ncurses/tinfo/lib_win32util.c
new file mode 100644
index 0000000..c67c8ce
--- /dev/null
+++ b/ncurses/tinfo/lib_win32util.c
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * Copyright 2020-2021,2023 Thomas E. Dickey                                *
+ * Copyright 1998-2009,2010 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            *
+ * "Software"), to deal in the Software without restriction, including      *
+ * without limitation the rights to use, copy, modify, merge, publish,      *
+ * distribute, distribute with modifications, sublicense, and/or sell       *
+ * copies of the Software, and to permit persons to whom the Software is    *
+ * furnished to do so, subject to the following conditions:                 *
+ *                                                                          *
+ * The above copyright notice and this permission notice shall be included  *
+ * in all copies or substantial portions of the Software.                   *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
+ * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
+ *                                                                          *
+ * Except as contained in this notice, the name(s) of the above copyright   *
+ * holders shall not be used in advertising or otherwise to promote the     *
+ * sale, use or other dealings in this Software without prior written       *
+ * authorization.                                                           *
+ ****************************************************************************/
+
+/****************************************************************************
+ *  Author: Juergen Pfeifer                                                 *
+ *     and: Thomas E. Dickey                                                *
+ ****************************************************************************/
+
+#include <curses.priv.h>
+
+MODULE_ID("$Id: lib_win32util.c,v 1.4 2023/06/17 17:19:06 tom Exp $")
+
+#ifdef _NC_WINDOWS
+#include <io.h>
+
+#ifdef _NC_CHECK_MINTTY
+#define PSAPI_VERSION 2
+#include <psapi.h>
+#include <tchar.h>
+
+#define array_length(a) (sizeof(a)/sizeof(a[0]))
+
+/*   This function tests, whether or not the ncurses application
+     is running as a descendant of MSYS2/cygwin mintty terminal
+     application. mintty doesn't use Windows Console for its screen
+     I/O, so the native Windows _isatty doesn't recognize it as
+     character device. But we can discover we are at the end of an
+     Pipe and can query the server side of the pipe, looking whether
+     or not this is mintty.
+     For now we terminate the program if we discover that situation.
+     Although in theory it would be possible, to remotely manipulate
+     the terminal state of mintty, this is out of scope for now and
+     not worth the significant effort.
+ */
+NCURSES_EXPORT(int)
+_nc_console_checkmintty(int fd, LPHANDLE pMinTTY)
+{
+    HANDLE handle = _nc_console_handle(fd);
+    DWORD dw;
+    int code = 0;
+
+    T((T_CALLED("lib_winhelper::_nc_console_checkmintty(%d, %p)"), fd, pMinTTY));
+
+    if (handle != INVALID_HANDLE_VALUE) {
+	dw = GetFileType(handle);
+	if (dw == FILE_TYPE_PIPE) {
+	    if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
+		ULONG pPid;
+		/* Requires NT6 */
+		if (GetNamedPipeServerProcessId(handle, &pPid)) {
+		    TCHAR buf[MAX_PATH];
+		    DWORD len = 0;
+		    /* These security attributes may allow us to
+		       create a remote thread in mintty to manipulate
+		       the terminal state remotely */
+		    HANDLE pHandle = OpenProcess(PROCESS_CREATE_THREAD
+						 | PROCESS_QUERY_INFORMATION
+						 | PROCESS_VM_OPERATION
+						 | PROCESS_VM_WRITE
+						 | PROCESS_VM_READ,
+						 FALSE,
+						 pPid);
+		    if (pMinTTY)
+			*pMinTTY = INVALID_HANDLE_VALUE;
+		    if (pHandle != INVALID_HANDLE_VALUE) {
+			if ((len = GetProcessImageFileName(pHandle,
+							   buf,
+							   (DWORD)
+							   array_length(buf)))) {
+			    TCHAR *pos = _tcsrchr(buf, _T('\\'));
+			    if (pos) {
+				pos++;
+				if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
+				    == 0) {
+				    if (pMinTTY)
+					*pMinTTY = pHandle;
+				    code = 1;
+				}
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+    returnCode(code);
+}
+#endif /* _NC_CHECK_MINTTY */
+
+#if HAVE_GETTIMEOFDAY == 2
+#define JAN1970 116444736000000000LL	/* the value for 01/01/1970 00:00 */
+
+NCURSES_EXPORT(int)
+_nc_gettimeofday(struct timeval *tv, void *tz GCC_UNUSED)
+{
+    union {
+	FILETIME ft;
+	long long since1601;	/* time since 1 Jan 1601 in 100ns units */
+    } data;
+
+    GetSystemTimeAsFileTime(&data.ft);
+    tv->tv_usec = (long) ((data.since1601 / 10LL) % 1000000LL);
+    tv->tv_sec = (long) ((data.since1601 - JAN1970) / 10000000LL);
+    return (0);
+}
+#endif // HAVE_GETTIMEOFDAY == 2
+
+#endif // _NC_WINDOWS
diff --git a/ncurses/tinfo/make_hash.c b/ncurses/tinfo/make_hash.c
index 37ac765..78a684c 100644
--- a/ncurses/tinfo/make_hash.c
+++ b/ncurses/tinfo/make_hash.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2020,2024 Thomas E. Dickey                                *
+ * Copyright 2009-2013,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            *
@@ -34,7 +35,6 @@
 
 /*
  *	make_hash.c --- build-time program for constructing comp_captab.c
- *
  */
 
 #include <build.priv.h>
@@ -44,14 +44,14 @@
 
 #include <ctype.h>
 
-MODULE_ID("$Id: make_hash.c,v 1.13 2013/09/28 20:55:47 tom Exp $")
+MODULE_ID("$Id: make_hash.c,v 1.34 2024/03/02 19:35:40 tom Exp $")
 
 /*
  *	_nc_make_hash_table()
  *
  *	Takes the entries in table[] and hashes them into hash_table[]
- *	by name.  There are CAPTABSIZE entries in table[] and HASHTABSIZE
- *	slots in hash_table[].
+ *	by name.  There are CAPTABSIZE entries in the predefined table[]
+ *	and HASHTABSIZE slots in hash_table[].
  *
  */
 
@@ -59,6 +59,14 @@
 #define MODULE_ID(id)		/*nothing */
 #include <tinfo/doalloc.c>
 
+#define L_PAREN "("
+#define R_PAREN ")"
+#define L_BRACE "{"
+#define R_BRACE "}"
+
+static const char *typenames[] =
+{"BOOLEAN", "NUMBER", "STRING"};
+
 static void
 failed(const char *s)
 {
@@ -82,7 +90,7 @@
  *
  *	Computes the hashing function on the given string.
  *
- *	The current hash function is the sum of each consectutive pair
+ *	The current hash function is the sum of each consecutive pair
  *	of characters, taken as two-byte integers, mod HASHTABSIZE.
  *
  */
@@ -93,36 +101,40 @@
     long sum = 0;
 
     while (*string) {
-	sum += (long) (*string + (*(string + 1) << 8));
+	sum += (long) (UChar(*string) + (UChar(*(string + 1)) << 8));
 	string++;
     }
 
     return (int) (sum % HASHTABSIZE);
 }
 
+#define UNUSED -1
+
 static void
-_nc_make_hash_table(struct name_table_entry *table,
-		    HashValue * hash_table)
+_nc_make_hash_table(struct user_table_entry *table,
+		    HashValue * hash_table,
+		    unsigned tablesize)
 {
-    short i;
+    unsigned i;
     int hashvalue;
     int collisions = 0;
 
     for (i = 0; i < HASHTABSIZE; i++) {
-	hash_table[i] = -1;
+	hash_table[i] = UNUSED;
     }
-    for (i = 0; i < CAPTABSIZE; i++) {
-	hashvalue = hash_function(table[i].nte_name);
+    for (i = 0; i < tablesize; i++) {
+	hashvalue = hash_function(table[i].ute_name);
 
 	if (hash_table[hashvalue] >= 0)
 	    collisions++;
 
-	if (hash_table[hashvalue] != 0)
-	    table[i].nte_link = hash_table[hashvalue];
-	hash_table[hashvalue] = i;
+	if (hash_table[hashvalue] != UNUSED) {
+	    table[i].ute_link = hash_table[hashvalue];
+	}
+	hash_table[hashvalue] = (HashValue) i;
     }
 
-    printf("/* %d collisions out of %d entries */\n", collisions, CAPTABSIZE);
+    printf("/* %d collisions out of %d entries */\n", collisions, tablesize);
 }
 
 /*
@@ -156,10 +168,18 @@
 
     int col = 0;
 
-    if (list == 0 && (list = typeCalloc(char *, (MAX_COLUMNS + 1))) == 0)
-	  return (0);
+    if (buffer == 0) {
+	free(list);
+	list = 0;
+	return 0;
+    }
 
     if (*buffer != '#') {
+	if (list == 0) {
+	    list = typeCalloc(char *, (MAX_COLUMNS + 1));
+	    if (list == 0)
+		return (0);
+	}
 	while (*buffer != '\0') {
 	    char *s;
 	    for (s = buffer; (*s != '\0') && !isspace(UChar(*s)); s++)
@@ -188,21 +208,58 @@
     return col ? list : 0;
 }
 
+#define SetType(n,t) \
+	if (is_user) \
+	    name_table[n].ute_type |= (int)(1 << (t)); \
+	else \
+	    name_table[n].ute_type = (t)
+
+#define GetType(n) \
+	(is_user \
+	 ? get_type(name_table[n].ute_type) \
+	 : typenames[name_table[n].ute_type])
+
+static char *
+get_type(int type_mask)
+{
+    static char result[80];
+    unsigned n;
+    _nc_STRCPY(result, L_PAREN, sizeof(result));
+    for (n = 0; n < 3; ++n) {
+	if ((1 << n) & type_mask) {
+	    size_t want = 5 + strlen(typenames[n]);
+	    if (want > sizeof(result)) {
+		fprintf(stderr, "Buffer is not large enough for %s + %s\n",
+			result, typenames[n]);
+		exit(EXIT_FAILURE);
+	    }
+	    if (result[1])
+		_nc_STRCAT(result, "|", sizeof(result));
+	    _nc_STRCAT(result, "1<<", sizeof(result));
+	    _nc_STRCAT(result, typenames[n], sizeof(result));
+	}
+    }
+    _nc_STRCAT(result, R_PAREN, sizeof(result));
+    return result;
+}
+
 int
 main(int argc, char **argv)
 {
-    struct name_table_entry *name_table = typeCalloc(struct
-						     name_table_entry, CAPTABSIZE);
+    unsigned tablesize = CAPTABSIZE;
+    struct user_table_entry *name_table = typeCalloc(struct
+						     user_table_entry, tablesize);
     HashValue *hash_table = typeCalloc(HashValue, HASHTABSIZE);
-    const char *root_name = "";
+    const char *root_name;
     int column = 0;
     int bigstring = 0;
-    int n;
+    unsigned n;
+    unsigned nn;
+    unsigned tableused = 0;
+    bool is_user;
+    const char *table_name;
     char buffer[BUFSIZ];
 
-    static const char *typenames[] =
-    {"BOOLEAN", "NUMBER", "STRING"};
-
     short BoolCount = 0;
     short NumCount = 0;
     short StrCount = 0;
@@ -220,42 +277,80 @@
 	fprintf(stderr, "usage: make_hash column root_name bigstring\n");
 	exit(EXIT_FAILURE);
     }
+    is_user = (*root_name == 'u');
+    table_name = (is_user ? "user" : "name");
 
     /*
      * Read the table into our arrays.
      */
-    for (n = 0; (n < CAPTABSIZE) && fgets(buffer, BUFSIZ, stdin);) {
-	char **list, *nlp = strchr(buffer, '\n');
+    for (n = 0; (n < tablesize) && fgets(buffer, BUFSIZ, stdin);) {
+	char **list;
+	char *nlp = strchr(buffer, '\n');
 	if (nlp)
 	    *nlp = '\0';
+	else
+	    buffer[sizeof(buffer) - 2] = '\0';
 	list = parse_columns(buffer);
 	if (list == 0)		/* blank or comment */
 	    continue;
-	if (column > count_columns(list)) {
+	if (is_user) {
+	    if (strcmp(list[0], "userdef"))
+		continue;
+	} else if (!strcmp(list[0], "userdef")) {
+	    continue;
+	}
+	if (column < 0 || column > count_columns(list)) {
 	    fprintf(stderr, "expected %d columns, have %d:\n%s\n",
 		    column,
 		    count_columns(list),
 		    buffer);
 	    exit(EXIT_FAILURE);
 	}
-	name_table[n].nte_link = -1;	/* end-of-hash */
-	name_table[n].nte_name = strmalloc(list[column]);
+	nn = tableused;
+	if (is_user) {
+	    unsigned j;
+	    for (j = 0; j < tableused; ++j) {
+		if (!strcmp(list[column], name_table[j].ute_name)) {
+		    nn = j;
+		    break;
+		}
+	    }
+	}
+	if (nn == tableused) {
+	    name_table[nn].ute_link = -1;	/* end-of-hash */
+	    name_table[nn].ute_name = strmalloc(list[column]);
+	    ++tableused;
+	}
+
 	if (!strcmp(list[2], "bool")) {
-	    name_table[n].nte_type = BOOLEAN;
-	    name_table[n].nte_index = BoolCount++;
+	    SetType(nn, BOOLEAN);
+	    name_table[nn].ute_index = BoolCount++;
 	} else if (!strcmp(list[2], "num")) {
-	    name_table[n].nte_type = NUMBER;
-	    name_table[n].nte_index = NumCount++;
+	    SetType(nn, NUMBER);
+	    name_table[nn].ute_index = NumCount++;
 	} else if (!strcmp(list[2], "str")) {
-	    name_table[n].nte_type = STRING;
-	    name_table[n].nte_index = StrCount++;
+	    SetType(nn, STRING);
+	    name_table[nn].ute_index = StrCount++;
+	    if (is_user) {
+		if (*list[3] != '-') {
+		    unsigned j;
+		    name_table[nn].ute_argc = (unsigned) strlen(list[3]);
+		    for (j = 0; j < name_table[nn].ute_argc; ++j) {
+			if (list[3][j] == 's') {
+			    name_table[nn].ute_args |= (1U << j);
+			}
+		    }
+		}
+	    }
 	} else {
 	    fprintf(stderr, "Unknown type: %s\n", list[2]);
 	    exit(EXIT_FAILURE);
 	}
 	n++;
     }
-    _nc_make_hash_table(name_table, hash_table);
+    if (tablesize > tableused)
+	tablesize = tableused;
+    _nc_make_hash_table(name_table, hash_table, tablesize);
 
     /*
      * Write the compiled tables to standard output
@@ -265,66 +360,86 @@
 	int nxt;
 
 	printf("static const char %s_names_text[] = \\\n", root_name);
-	for (n = 0; n < CAPTABSIZE; n++) {
-	    nxt = (int) strlen(name_table[n].nte_name) + 5;
+	for (n = 0; n < tablesize; n++) {
+	    nxt = (int) strlen(name_table[n].ute_name) + 5;
 	    if (nxt + len > 72) {
 		printf("\\\n");
 		len = 0;
 	    }
-	    printf("\"%s\\0\" ", name_table[n].nte_name);
+	    printf("\"%s\\0\" ", name_table[n].ute_name);
 	    len += nxt;
 	}
 	printf(";\n\n");
 
 	len = 0;
-	printf("static name_table_data const %s_names_data[] =\n",
+	printf("static %s_table_data const %s_names_data[] =\n",
+	       table_name,
 	       root_name);
-	printf("{\n");
-	for (n = 0; n < CAPTABSIZE; n++) {
-	    printf("\t{ %15d,\t%10s,\t%3d, %3d }%c\n",
-		   len,
-		   typenames[name_table[n].nte_type],
-		   name_table[n].nte_index,
-		   name_table[n].nte_link,
-		   n < CAPTABSIZE - 1 ? ',' : ' ');
-	    len += (int) strlen(name_table[n].nte_name) + 1;
+	printf("%s\n", L_BRACE);
+	for (n = 0; n < tablesize; n++) {
+	    printf("\t%s %15d,\t%10s,", L_BRACE, len, GetType(n));
+	    if (is_user)
+		printf("\t%d,%d,",
+		       name_table[n].ute_argc,
+		       name_table[n].ute_args);
+	    printf("\t%3d, %3d %s%c\n",
+		   name_table[n].ute_index,
+		   name_table[n].ute_link,
+		   R_BRACE,
+		   n < tablesize - 1 ? ',' : ' ');
+	    len += (int) strlen(name_table[n].ute_name) + 1;
 	}
-	printf("};\n\n");
-	printf("static struct name_table_entry *_nc_%s_table = 0;\n\n", root_name);
+	printf("%s;\n\n", R_BRACE);
+	printf("static struct %s_table_entry *_nc_%s_table = 0;\n\n",
+	       table_name,
+	       root_name);
     } else {
 
-	printf("static struct name_table_entry const _nc_%s_table[] =\n",
+	printf("static struct %s_table_entry const _nc_%s_table[] =\n",
+	       table_name,
 	       root_name);
-	printf("{\n");
-	for (n = 0; n < CAPTABSIZE; n++) {
+	printf("%s\n", L_BRACE);
+	for (n = 0; n < tablesize; n++) {
 	    _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer)) "\"%s\"",
-			name_table[n].nte_name);
-	    printf("\t{ %15s,\t%10s,\t%3d, %3d }%c\n",
-		   buffer,
-		   typenames[name_table[n].nte_type],
-		   name_table[n].nte_index,
-		   name_table[n].nte_link,
-		   n < CAPTABSIZE - 1 ? ',' : ' ');
+			name_table[n].ute_name);
+	    printf("\t%s %15s,\t%10s,", L_BRACE, buffer, GetType(n));
+	    if (is_user)
+		printf("\t%d,%d,",
+		       name_table[n].ute_argc,
+		       name_table[n].ute_args);
+	    printf("\t%3d, %3d %s%c\n",
+		   name_table[n].ute_index,
+		   name_table[n].ute_link,
+		   R_BRACE,
+		   n < tablesize - 1 ? ',' : ' ');
 	}
-	printf("};\n\n");
+	printf("%s;\n\n", R_BRACE);
     }
 
     printf("static const HashValue _nc_%s_hash_table[%d] =\n",
 	   root_name,
 	   HASHTABSIZE + 1);
-    printf("{\n");
+    printf("%s\n", L_BRACE);
     for (n = 0; n < HASHTABSIZE; n++) {
 	printf("\t%3d,\n", hash_table[n]);
     }
     printf("\t0\t/* base-of-table */\n");
-    printf("};\n\n");
+    printf("%s;\n\n", R_BRACE);
 
-    printf("#if (BOOLCOUNT!=%d)||(NUMCOUNT!=%d)||(STRCOUNT!=%d)\n",
-	   BoolCount, NumCount, StrCount);
-    printf("#error\t--> term.h and comp_captab.c disagree about the <--\n");
-    printf("#error\t--> numbers of booleans, numbers and/or strings <--\n");
-    printf("#endif\n\n");
+    if (!is_user) {
+	printf("#if (BOOLCOUNT!=%d)||(NUMCOUNT!=%d)||(STRCOUNT!=%d)\n",
+	       BoolCount, NumCount, StrCount);
+	printf("#error\t--> term.h and comp_captab.c disagree about the <--\n");
+	printf("#error\t--> numbers of booleans, numbers and/or strings <--\n");
+	printf("#endif\n\n");
+    }
 
     free(hash_table);
+    for (n = 0; (n < tablesize); ++n) {
+	free((void *) name_table[n].ute_name);
+    }
+    free(name_table);
+    parse_columns(0);
+
     return EXIT_SUCCESS;
 }
diff --git a/ncurses/tinfo/make_keys.c b/ncurses/tinfo/make_keys.c
index fa0c2f2..b8eb934 100644
--- a/ncurses/tinfo/make_keys.c
+++ b/ncurses/tinfo/make_keys.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2015 Free Software Foundation, Inc.              *
+ * Copyright 2020,2021 Thomas E. Dickey                                     *
+ * Copyright 1998-2011,2015 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            *
@@ -39,7 +40,7 @@
 #define USE_TERMLIB 1
 #include <build.priv.h>
 
-MODULE_ID("$Id: make_keys.c,v 1.21 2015/07/16 01:10:03 tom Exp $")
+MODULE_ID("$Id: make_keys.c,v 1.23 2021/08/18 20:55:25 tom Exp $")
 
 #include <names.c>
 
@@ -127,11 +128,13 @@
 {
     static const char *prefix[] =
     {
-	"#ifndef NCU_KEYS_H",
-	"#define NCU_KEYS_H 1",
+	"#ifndef _INIT_KEYTRY_H",
+	"#define _INIT_KEYTRY_H 1",
 	"",
 	"/* This file was generated by MAKE_KEYS */",
 	"",
+	"#include <tic.h>",
+	"",
 	"#if BROKEN_LINKER",
 	"static",
 	"#endif",
@@ -142,7 +145,7 @@
     {
 	"\t{ 0, 0} };",
 	"",
-	"#endif /* NCU_KEYS_H */",
+	"#endif /* _INIT_KEYTRY_H */",
 	0
     };
 
diff --git a/ncurses/tinfo/name_match.c b/ncurses/tinfo/name_match.c
index c648535..71e584d 100644
--- a/ncurses/tinfo/name_match.c
+++ b/ncurses/tinfo/name_match.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1999-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2020 Thomas E. Dickey                                          *
+ * Copyright 1998-2013,2016 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            *
@@ -33,7 +34,7 @@
 #include <curses.priv.h>
 #include <tic.h>
 
-MODULE_ID("$Id: name_match.c,v 1.23 2013/05/25 20:20:08 tom Exp $")
+MODULE_ID("$Id: name_match.c,v 1.25 2020/02/02 23:34:34 tom Exp $")
 
 #define FirstName _nc_globals.first_name
 
@@ -58,8 +59,6 @@
 NCURSES_EXPORT(char *)
 _nc_first_name(const char *const sp)
 {
-    unsigned n;
-
 #if NO_LEAKS
     if (sp == 0) {
 	if (FirstName != 0) {
@@ -72,6 +71,7 @@
 	    FirstName = typeMalloc(char, MAX_NAME_SIZE + 1);
 
 	if (FirstName != 0) {
+	    unsigned n;
 	    const char *src = sp;
 #if NCURSES_USE_TERMCAP && NCURSES_XNAMES
 	    src = skip_index(sp);
@@ -93,11 +93,13 @@
 NCURSES_EXPORT(int)
 _nc_name_match(const char *const namelst, const char *const name, const char *const delim)
 {
-    const char *s, *d, *t;
-    int code, found;
+    const char *s;
 
     if ((s = namelst) != 0) {
 	while (*s != '\0') {
+	    const char *d, *t;
+	    int code, found;
+
 	    for (d = name; *d != '\0'; d++) {
 		if (*s != *d)
 		    break;
diff --git a/ncurses/tinfo/obsolete.c b/ncurses/tinfo/obsolete.c
index 63476dc..cc62f18 100644
--- a/ncurses/tinfo/obsolete.c
+++ b/ncurses/tinfo/obsolete.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2013,2014 Free Software Foundation, Inc.                   *
+ * Copyright 2020,2023 Thomas E. Dickey                                     *
+ * Copyright 2013-2014,2016 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            *
@@ -36,10 +37,10 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: obsolete.c,v 1.3 2014/10/11 02:39:35 tom Exp $")
+MODULE_ID("$Id: obsolete.c,v 1.11 2023/10/21 15:38:47 tom Exp $")
 
 /*
- * Obsolete entrypoint retained for binary compatbility.
+ * Obsolete entrypoint retained for binary compatibility.
  */
 NCURSES_EXPORT(void)
 NCURSES_SP_NAME(_nc_set_buffer) (NCURSES_SP_DCLx FILE *ofp, int buffered)
@@ -68,7 +69,7 @@
 	size_t need = strlen(s);
 	result = malloc(need + 1);
 	if (result != 0) {
-	    strcpy(result, s);
+	    _nc_STRCPY(result, s, need);
 	}
     }
     return result;
@@ -237,3 +238,98 @@
 #undef CH
 }
 #endif /* EXP_XTERM_1005 */
+
+#ifdef EXP_OOM_TESTING
+/*
+ * Out-of-memory testing, suitable for checking if initialization (and limited
+ * running) recovers from errors due to insufficient memory.  In practice, this
+ * is unlikely except with artificially constructed tests (or poorly behaved
+ * applications).
+ */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef strdup
+
+#define TR_OOM(stmt) T(stmt)
+
+static long oom_limit = -1;
+static long oom_count = 0;
+
+static bool
+oom_check(void)
+{
+    static bool initialized = FALSE;
+    static bool triggered = FALSE;
+    bool result = FALSE;
+
+    if (!initialized) {
+	char *env = getenv("NCURSES_OOM_TESTING");
+	initialized = TRUE;
+	if (env != NULL) {
+	    char *check;
+	    oom_limit = strtol(env, &check, 0);
+	    if (check != NULL && *check != '\0')
+		oom_limit = 0;
+	}
+    }
+    ++oom_count;
+    if (oom_limit >= 0) {
+	result = (oom_count > oom_limit);
+	if (result && !triggered) {
+	    triggered = TRUE;
+	    TR_OOM(("out-of-memory"));
+	}
+    }
+    return result;
+}
+
+NCURSES_EXPORT(void *)
+_nc_oom_malloc(size_t size)
+{
+    char *result = (oom_check()
+		    ? NULL
+		    : malloc(size));
+    TR_OOM(("oom #%ld malloc(%ld) %p", oom_count, size, result));
+    return result;
+}
+
+NCURSES_EXPORT(void *)
+_nc_oom_calloc(size_t nmemb, size_t size)
+{
+    char *result = (oom_check()
+		    ? NULL
+		    : calloc(nmemb, size));
+    TR_OOM(("oom #%ld calloc(%ld, %ld) %p", oom_count, nmemb, size, result));
+    return result;
+}
+
+NCURSES_EXPORT(void *)
+_nc_oom_realloc(void *ptr, size_t size)
+{
+    char *result = (oom_check()
+		    ? NULL
+		    : realloc(ptr, size));
+    TR_OOM(("oom #%ld realloc(%p, %ld) %p", oom_count, ptr, size, result));
+    return result;
+}
+
+NCURSES_EXPORT(void)
+_nc_oom_free(void *ptr)
+{
+    ++oom_count;
+    TR_OOM(("oom #%ld free(%p)", oom_count, ptr));
+    free(ptr);
+}
+
+NCURSES_EXPORT(char *)
+_nc_oom_strdup(const char *ptr)
+{
+    char *result = (oom_check()
+		    ? NULL
+		    : strdup(ptr));
+    TR_OOM(("oom #%ld strdup(%p) %p", oom_count, ptr, result));
+    return result;
+}
+#endif /* EXP_OOM_TESTING */
diff --git a/ncurses/tinfo/parse_entry.c b/ncurses/tinfo/parse_entry.c
index 0dc1414..13fef49 100644
--- a/ncurses/tinfo/parse_entry.c
+++ b/ncurses/tinfo/parse_entry.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2011,2012 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 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,7 +48,7 @@
 #include <ctype.h>
 #include <tic.h>
 
-MODULE_ID("$Id: parse_entry.c,v 1.80 2015/04/04 14:18:38 tom Exp $")
+MODULE_ID("$Id: parse_entry.c,v 1.108 2023/04/24 22:32:33 tom Exp $")
 
 #ifdef LINT
 static short const parametrized[] =
@@ -56,17 +57,17 @@
 #include <parametrized.h>
 #endif
 
-static void postprocess_termcap(TERMTYPE *, bool);
-static void postprocess_terminfo(TERMTYPE *);
+static void postprocess_termcap(TERMTYPE2 *, bool);
+static void postprocess_terminfo(TERMTYPE2 *);
 static struct name_table_entry const *lookup_fullname(const char *name);
 
 #if NCURSES_XNAMES
 
 static struct name_table_entry const *
-_nc_extend_names(ENTRY * entryp, char *name, int token_type)
+_nc_extend_names(ENTRY * entryp, const char *name, int token_type)
 {
     static struct name_table_entry temp;
-    TERMTYPE *tp = &(entryp->tterm);
+    TERMTYPE2 *tp = &(entryp->tterm);
     unsigned offset = 0;
     unsigned actual;
     unsigned tindex;
@@ -109,7 +110,7 @@
 	/* Well, we are given a cancel for a name that we don't recognize */
 	return _nc_extend_names(entryp, name, STRING);
     default:
-	return 0;
+	return NULL;
     }
 
     /* Adjust the 'offset' (insertion-point) to keep the lists of extended
@@ -141,6 +142,11 @@
 	for (last = (unsigned) (max - 1); last > tindex; last--)
 
     if (!found) {
+	char *saved;
+
+	if ((saved = _nc_save_str(name)) == NULL)
+	    return NULL;
+
 	switch (token_type) {
 	case BOOLEAN:
 	    tp->ext_Booleans++;
@@ -152,7 +158,7 @@
 	case NUMBER:
 	    tp->ext_Numbers++;
 	    tp->num_Numbers++;
-	    TYPE_REALLOC(short, tp->num_Numbers, tp->Numbers);
+	    TYPE_REALLOC(NCURSES_INT2, tp->num_Numbers, tp->Numbers);
 	    for_each_value(tp->num_Numbers)
 		tp->Numbers[last] = tp->Numbers[last - 1];
 	    break;
@@ -168,7 +174,7 @@
 	TYPE_REALLOC(char *, actual, tp->ext_Names);
 	while (--actual > offset)
 	    tp->ext_Names[actual] = tp->ext_Names[actual - 1];
-	tp->ext_Names[offset] = _nc_save_str(name);
+	tp->ext_Names[offset] = saved;
     }
 
     temp.nte_name = tp->ext_Names[offset];
@@ -178,9 +184,79 @@
 
     return &temp;
 }
+
+static const char *
+usertype2s(int mask)
+{
+    const char *result = "unknown";
+    if (mask & (1 << BOOLEAN)) {
+	result = "boolean";
+    } else if (mask & (1 << NUMBER)) {
+	result = "number";
+    } else if (mask & (1 << STRING)) {
+	result = "string";
+    }
+    return result;
+}
+
+static bool
+expected_type(const char *name, int token_type, bool silent)
+{
+    struct user_table_entry const *entry = _nc_find_user_entry(name);
+    bool result = TRUE;
+    if ((entry != 0) && (token_type != CANCEL)) {
+	int have_type = (1 << token_type);
+	if (!(entry->ute_type & have_type)) {
+	    if (!silent)
+		_nc_warning("expected %s-type for %s, have %s",
+			    usertype2s(entry->ute_type),
+			    name,
+			    usertype2s(have_type));
+	    result = FALSE;
+	}
+    }
+    return result;
+}
 #endif /* NCURSES_XNAMES */
 
 /*
+ * A valid entry name uses characters from the "portable character set"
+ * (more commonly referred to as US-ASCII), and disallows some of the
+ * punctuation characters:
+ *
+ * '/' is a pathname separator
+ * '\' may be a pathname separator, but more important, is an escape
+ * '|' delimits names and description
+ * '#' denotes a numeric value
+ * '=' denotes a string value
+ * '@' denotes a cancelled symbol
+ * ',' separates terminfo capabilities
+ * ':' separates termcap capabilities
+ *
+ * Termcap capability names may begin with a '#' or '@' (since they have
+ * exactly two characters).
+ */
+static bool
+valid_entryname(const char *name)
+{
+    bool result = TRUE;
+    bool first = TRUE;
+    int ch;
+    while ((ch = UChar(*name++)) != '\0') {
+	if (ch <= ' ' || ch > '~' || strchr("/\\|=,:", ch) != NULL) {
+	    result = FALSE;
+	    break;
+	}
+	if (!first && strchr("#@", ch) != NULL) {
+	    result = FALSE;
+	    break;
+	}
+	first = FALSE;
+    }
+    return result;
+}
+
+/*
  *	int
  *	_nc_parse_entry(entry, literal, silent)
  *
@@ -203,24 +279,29 @@
  	{ bad_tc_usage = TRUE; \
 	 _nc_warning("Legacy termcap allows only a trailing tc= clause"); }
 
-#define MAX_NUMBER 0x7fff	/* positive shorts only */
+#define MAX_NUMBER MAX_OF_TYPE(NCURSES_INT2)
 
 NCURSES_EXPORT(int)
-_nc_parse_entry(struct entry *entryp, int literal, bool silent)
+_nc_parse_entry(ENTRY * entryp, int literal, bool silent)
 {
     int token_type;
     struct name_table_entry const *entry_ptr;
     char *ptr, *base;
+    const char *name;
     bool bad_tc_usage = FALSE;
 
+    TR(TRACE_DATABASE,
+       (T_CALLED("_nc_parse_entry(entry=%p, literal=%d, silent=%d)"),
+	(void *) entryp, literal, silent));
+
     token_type = _nc_get_token(silent);
 
     if (token_type == EOF)
-	return (EOF);
+	returnDB(EOF);
     if (token_type != NAMES)
 	_nc_err_abort("Entry does not start with terminal names in column one");
 
-    _nc_init_entry(&entryp->tterm);
+    _nc_init_entry(entryp);
 
     entryp->cstart = _nc_comment_start;
     entryp->cend = _nc_comment_end;
@@ -236,13 +317,14 @@
      * implemented it.  Note that the resulting terminal type was never the
      * 2-character name, but was instead the first alias after that.
      */
+#define ok_TC2(s) (isgraph(UChar(s)) && (s) != '|')
     ptr = _nc_curr_token.tk_name;
     if (_nc_syntax == SYN_TERMCAP
 #if NCURSES_XNAMES
 	&& !_nc_user_definable
 #endif
 	) {
-	if (ptr[2] == '|') {
+	if (ok_TC2(ptr[0]) && ok_TC2(ptr[1]) && (ptr[2] == '|')) {
 	    ptr += 3;
 	    _nc_curr_token.tk_name[2] = '\0';
 	}
@@ -251,16 +333,21 @@
     entryp->tterm.str_table = entryp->tterm.term_names = _nc_save_str(ptr);
 
     if (entryp->tterm.str_table == 0)
-	return (ERR);
+	returnDB(ERR);
 
-    DEBUG(1, ("Starting '%s'", ptr));
+    DEBUG(2, ("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));
+    name = _nc_first_name(entryp->tterm.term_names);
+    if (!valid_entryname(name)) {
+	_nc_warning("invalid entry name \"%s\"", name);
+	name = "invalid";
+    }
+    _nc_set_type(name);
 
     /* check for overly-long names and aliases */
     for (base = entryp->tterm.term_names; (ptr = strchr(base, '|')) != 0;
@@ -282,11 +369,28 @@
 	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
+	    char *saved;
+
+	    if (!VALID_STRING(_nc_curr_token.tk_valstring)
+		|| _nc_curr_token.tk_valstring[0] == '\0') {
+		_nc_warning("missing name for use-clause");
+		continue;
+	    } else if (!valid_entryname(_nc_curr_token.tk_valstring)) {
+		_nc_warning("invalid name for use-clause \"%s\"",
+			    _nc_curr_token.tk_valstring);
+		continue;
+	    } else if (entryp->nuses >= MAX_USES) {
+		_nc_warning("too many use-clauses, ignored \"%s\"",
+			    _nc_curr_token.tk_valstring);
+		continue;
+	    }
+	    if ((saved = _nc_save_str(_nc_curr_token.tk_valstring)) != NULL) {
+		entryp->uses[entryp->nuses].name = saved;
+		entryp->uses[entryp->nuses].line = _nc_curr_line;
+		entryp->nuses++;
+		if (entryp->nuses > 1 && is_tc) {
+		    BAD_TC_USAGE
+		}
 	    }
 	} else {
 	    /* normal token lookup */
@@ -294,7 +398,7 @@
 				       _nc_get_hash_table(_nc_syntax));
 
 	    /*
-	     * Our kluge to handle aliasing.  The reason it's done
+	     * Our kluge to handle aliasing.  The reason it is 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
@@ -351,12 +455,20 @@
 	     * 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);
+		&& _nc_user_definable) {
+		if (expected_type(_nc_curr_token.tk_name, token_type, silent)) {
+		    if ((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);
+			}
+		    }
+		} else {
+		    /* ignore it: we have already printed error message */
+		    continue;
+		}
 	    }
 #endif /* NCURSES_XNAMES */
 
@@ -369,7 +481,16 @@
 	    }
 
 	    /* deal with bad type/value combinations. */
-	    if (token_type != CANCEL && entry_ptr->nte_type != token_type) {
+	    if (token_type == CANCEL) {
+		/*
+		 * Prefer terminfo in this (long-obsolete) ambiguity:
+		 */
+		if (!strcmp("ma", _nc_curr_token.tk_name)) {
+		    entry_ptr = _nc_find_type_entry("ma", NUMBER,
+						    _nc_syntax != 0);
+		    assert(entry_ptr != 0);
+		}
+	    } else if (entry_ptr->nte_type != token_type) {
 		/*
 		 * Nasty special cases here handle situations in which type
 		 * information can resolve name clashes.  Normal lookup
@@ -446,20 +567,27 @@
 		break;
 
 	    case NUMBER:
+#if !NCURSES_EXT_NUMBERS
 		if (_nc_curr_token.tk_valnumber > MAX_NUMBER) {
 		    entryp->tterm.Numbers[entry_ptr->nte_index] = MAX_NUMBER;
-		} else {
+		} else
+#endif
+		{
 		    entryp->tterm.Numbers[entry_ptr->nte_index] =
-			(short) _nc_curr_token.tk_valnumber;
+			(NCURSES_INT2) _nc_curr_token.tk_valnumber;
 		}
 		break;
 
 	    case STRING:
 		ptr = _nc_curr_token.tk_valstring;
-		if (_nc_syntax == SYN_TERMCAP)
+		if (_nc_syntax == SYN_TERMCAP) {
+		    int n = entry_ptr->nte_index;
 		    ptr = _nc_captoinfo(_nc_curr_token.tk_name,
 					ptr,
-					parametrized[entry_ptr->nte_index]);
+					(n < (int) SIZEOF(parametrized))
+					? parametrized[n]
+					: 0);
+		}
 		entryp->tterm.Strings[entry_ptr->nte_index] = _nc_save_str(ptr);
 		break;
 
@@ -486,23 +614,26 @@
     if (!literal) {
 	if (_nc_syntax == SYN_TERMCAP) {
 	    bool has_base_entry = FALSE;
-	    unsigned 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, '+'))
+	    if (strchr(entryp->tterm.term_names, '+')) {
 		has_base_entry = TRUE;
-	    else
+	    } else {
+		unsigned i;
 		/*
 		 * 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, '+'))
+		for (i = 0; i < entryp->nuses; i++) {
+		    if (entryp->uses[i].name != 0
+			&& !strchr(entryp->uses[i].name, '+'))
 			has_base_entry = TRUE;
+		}
+	    }
 
 	    postprocess_termcap(&entryp->tterm, has_base_entry);
 	} else
@@ -510,59 +641,66 @@
     }
     _nc_wrap_entry(entryp, FALSE);
 
-    return (OK);
+    returnDB(OK);
 }
 
 NCURSES_EXPORT(int)
 _nc_capcmp(const char *s, const char *t)
 /* compare two string capabilities, stripping out padding */
 {
-    if (!VALID_STRING(s) && !VALID_STRING(t))
-	return (0);
-    else if (!VALID_STRING(s) || !VALID_STRING(t))
-	return (1);
+    bool ok_s = VALID_STRING(s);
+    bool ok_t = VALID_STRING(t);
 
-    for (;;) {
-	if (s[0] == '$' && s[1] == '<') {
-	    for (s += 2;; s++)
-		if (!(isdigit(UChar(*s))
-		      || *s == '.'
-		      || *s == '*'
-		      || *s == '/'
-		      || *s == '>'))
-		    break;
+    if (ok_s && ok_t) {
+	for (;;) {
+	    if (s[0] == '$' && s[1] == '<') {
+		for (s += 2;; s++) {
+		    if (!(isdigit(UChar(*s))
+			  || *s == '.'
+			  || *s == '*'
+			  || *s == '/'
+			  || *s == '>')) {
+			break;
+		    }
+		}
+	    }
+
+	    if (t[0] == '$' && t[1] == '<') {
+		for (t += 2;; t++) {
+		    if (!(isdigit(UChar(*t))
+			  || *t == '.'
+			  || *t == '*'
+			  || *t == '/'
+			  || *t == '>')) {
+			break;
+		    }
+		}
+	    }
+
+	    /* we've now pushed s and t past any padding they pointed at */
+
+	    if (*s == '\0' && *t == '\0')
+		return (0);
+
+	    if (*s != *t)
+		return (*t - *s);
+
+	    /* else *s == *t but one is not NUL, so continue */
+	    s++, t++;
 	}
-
-	if (t[0] == '$' && t[1] == '<') {
-	    for (t += 2;; t++)
-		if (!(isdigit(UChar(*t))
-		      || *t == '.'
-		      || *t == '*'
-		      || *t == '/'
-		      || *t == '>'))
-		    break;
-	}
-
-	/* we've now pushed s and t past any padding they were pointing at */
-
-	if (*s == '\0' && *t == '\0')
-	    return (0);
-
-	if (*s != *t)
-	    return (*t - *s);
-
-	/* else *s == *t but one is not NUL, so continue */
-	s++, t++;
+    } else if (ok_s || ok_t) {
+	return 1;
     }
+    return 0;
 }
 
 static void
-append_acs0(string_desc * dst, int code, int src)
+append_acs0(string_desc * dst, int code, char *src, size_t off)
 {
-    if (src != 0) {
+    if (src != 0 && off < strlen(src)) {
 	char temp[3];
 	temp[0] = (char) code;
-	temp[1] = (char) src;
+	temp[1] = src[off];
 	temp[2] = 0;
 	_nc_safe_strcat(dst, temp);
     }
@@ -571,8 +709,8 @@
 static void
 append_acs(string_desc * dst, int code, char *src)
 {
-    if (src != 0 && strlen(src) == 1) {
-	append_acs0(dst, code, *src);
+    if (VALID_STRING(src) && strlen(src) == 1) {
+	append_acs0(dst, code, src, 0);
     }
 }
 
@@ -621,13 +759,6 @@
 static const char C_HT[] = "\t";
 
 /*
- * Note that WANTED and PRESENT are not simple inverses!  If a capability
- * has been explicitly cancelled, it's not considered WANTED.
- */
-#define WANTED(s)	((s) == ABSENT_STRING)
-#define PRESENT(s)	(((s) != ABSENT_STRING) && ((s) != CANCELLED_STRING))
-
-/*
  * This bit of legerdemain turns all the terminfo variable names into
  * references to locations in the arrays Booleans, Numbers, and Strings ---
  * precisely what's needed.
@@ -637,11 +768,15 @@
 #define CUR tp->
 
 static void
-postprocess_termcap(TERMTYPE *tp, bool has_base)
+postprocess_termcap(TERMTYPE2 *tp, bool has_base)
 {
     char buf[MAX_LINE * 2 + 2];
     string_desc result;
 
+    TR(TRACE_DATABASE,
+       (T_CALLED("postprocess_termcap(tp=%p, has_base=%d)"),
+	(void *) tp, has_base));
+
     /*
      * TERMCAP DEFAULTS AND OBSOLETE-CAPABILITY TRANSLATIONS
      *
@@ -652,10 +787,10 @@
 
     /* if there was a tc entry, assume we picked up defaults via that */
     if (!has_base) {
-	if (WANTED(init_3string) && termcap_init2)
+	if (WANTED(init_3string) && PRESENT(termcap_init2))
 	    init_3string = _nc_save_str(termcap_init2);
 
-	if (WANTED(reset_2string) && termcap_reset)
+	if (WANTED(reset_2string) && PRESENT(termcap_reset))
 	    reset_2string = _nc_save_str(termcap_reset);
 
 	if (WANTED(carriage_return)) {
@@ -770,7 +905,7 @@
 	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))
+	    if (PRESENT(tab) && _nc_capcmp(tab, C_HT))
 		_nc_warning("hardware tabs with a non-^I tab string %s",
 			    _nc_visbuf(tab));
 	    else {
@@ -832,15 +967,14 @@
 	    }
 
 	    if (tp->Strings[to_ptr->nte_index]) {
-		/* There's no point in warning about it if it's the same
+		const char *s = tp->Strings[from_ptr->nte_index];
+		const char *t = tp->Strings[to_ptr->nte_index];
+		/* There's no point in warning about it if it is the same
 		 * string; that's just an inefficiency.
 		 */
-		if (strcmp(
-			      tp->Strings[from_ptr->nte_index],
-			      tp->Strings[to_ptr->nte_index]) != 0)
+		if (VALID_STRING(s) && VALID_STRING(t) && strcmp(s, t) != 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]));
+				ap->to, ap->from, t);
 		continue;
 	    }
 
@@ -848,17 +982,22 @@
 	     * 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';
+	    bp = tp->Strings[from_ptr->nte_index];
+	    if (VALID_STRING(bp)) {
+		for (dp = buf2; *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);
+		tp->Strings[to_ptr->nte_index] = _nc_save_str(buf2);
+	    } else {
+		tp->Strings[to_ptr->nte_index] = bp;
+	    }
 	}
 
 	/*
@@ -867,7 +1006,7 @@
 	 * 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) {
+	if (foundim && WANTED(key_ic) && PRESENT(key_sic)) {
 	    key_ic = key_sic;
 	    key_sic = ABSENT_STRING;
 	}
@@ -919,16 +1058,21 @@
 	    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) {
+    } else if (acs_chars == ABSENT_STRING
+	       && PRESENT(enter_alt_charset_mode)
+	       && PRESENT(exit_alt_charset_mode)) {
 	acs_chars = _nc_save_str(VT_ACSC);
     }
+    returnVoidDB;
 }
 
 static void
-postprocess_terminfo(TERMTYPE *tp)
+postprocess_terminfo(TERMTYPE2 *tp)
 {
+    TR(TRACE_DATABASE,
+       (T_CALLED("postprocess_terminfo(tp=%p)"),
+	(void *) tp));
+
     /*
      * TERMINFO-TO-TERMINFO MAPPINGS FOR SOURCE TRANSLATION
      * ----------------------------------------------------------------------
@@ -944,17 +1088,17 @@
 	_nc_str_init(&result, buf2, sizeof(buf2));
 	_nc_safe_strcat(&result, acs_chars);
 
-	append_acs0(&result, 'l', box_chars_1[0]);	/* ACS_ULCORNER */
-	append_acs0(&result, 'q', box_chars_1[1]);	/* ACS_HLINE */
-	append_acs0(&result, 'k', box_chars_1[2]);	/* ACS_URCORNER */
-	append_acs0(&result, 'x', box_chars_1[3]);	/* ACS_VLINE */
-	append_acs0(&result, 'j', box_chars_1[4]);	/* ACS_LRCORNER */
-	append_acs0(&result, 'm', box_chars_1[5]);	/* ACS_LLCORNER */
-	append_acs0(&result, 'w', box_chars_1[6]);	/* ACS_TTEE */
-	append_acs0(&result, 'u', box_chars_1[7]);	/* ACS_RTEE */
-	append_acs0(&result, 'v', box_chars_1[8]);	/* ACS_BTEE */
-	append_acs0(&result, 't', box_chars_1[9]);	/* ACS_LTEE */
-	append_acs0(&result, 'n', box_chars_1[10]);	/* ACS_PLUS */
+	append_acs0(&result, 'l', box_chars_1, 0);	/* ACS_ULCORNER */
+	append_acs0(&result, 'q', box_chars_1, 1);	/* ACS_HLINE */
+	append_acs0(&result, 'k', box_chars_1, 2);	/* ACS_URCORNER */
+	append_acs0(&result, 'x', box_chars_1, 3);	/* ACS_VLINE */
+	append_acs0(&result, 'j', box_chars_1, 4);	/* ACS_LRCORNER */
+	append_acs0(&result, 'm', box_chars_1, 5);	/* ACS_LLCORNER */
+	append_acs0(&result, 'w', box_chars_1, 6);	/* ACS_TTEE */
+	append_acs0(&result, 'u', box_chars_1, 7);	/* ACS_RTEE */
+	append_acs0(&result, 'v', box_chars_1, 8);	/* ACS_BTEE */
+	append_acs0(&result, 't', box_chars_1, 9);	/* ACS_LTEE */
+	append_acs0(&result, 'n', box_chars_1, 10);	/* ACS_PLUS */
 
 	if (buf2[0]) {
 	    acs_chars = _nc_save_str(buf2);
@@ -965,6 +1109,7 @@
     /*
      * ----------------------------------------------------------------------
      */
+    returnVoidDB;
 }
 
 /*
diff --git a/ncurses/tinfo/read_entry.c b/ncurses/tinfo/read_entry.c
index 81fdc46..b0f360e 100644
--- a/ncurses/tinfo/read_entry.c
+++ b/ncurses/tinfo/read_entry.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 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            *
@@ -41,15 +42,87 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: read_entry.c,v 1.129 2015/06/27 16:16:40 tom Exp $")
-
-#define TYPE_CALLOC(type,elts) typeCalloc(type, (unsigned)(elts))
+MODULE_ID("$Id: read_entry.c,v 1.171 2023/09/16 16:30:34 tom Exp $")
 
 #define MyNumber(n) (short) LOW_MSB(n)
 
+#define SIZEOF_32BITS 4
+
 #if NCURSES_USE_DATABASE
-static void
-convert_shorts(char *buf, short *Numbers, int count)
+#if NCURSES_EXT_NUMBERS
+static size_t
+convert_16bits(char *buf, NCURSES_INT2 *Numbers, int count)
+{
+    int i;
+    size_t j;
+    size_t size = SIZEOF_SHORT;
+    for (i = 0; i < count; i++) {
+	unsigned mask = 0xff;
+	unsigned char ch = 0;
+	Numbers[i] = 0;
+	for (j = 0; j < size; ++j) {
+	    ch = UChar(*buf++);
+	    Numbers[i] |= (ch << (8 * j));
+	    mask <<= 8;
+	}
+	if (ch & 0x80) {
+	    while (mask != 0) {
+		Numbers[i] |= (int) mask;
+		mask <<= 8;
+	    }
+	}
+	TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
+    }
+    return size;
+}
+
+static size_t
+convert_32bits(char *buf, NCURSES_INT2 *Numbers, int count)
+{
+    int i;
+    size_t j;
+    size_t size = SIZEOF_INT2;
+    unsigned char ch;
+
+    assert(sizeof(NCURSES_INT2) == size);
+    for (i = 0; i < count; i++) {
+	Numbers[i] = 0;
+	for (j = 0; j < size; ++j) {
+	    ch = UChar(*buf++);
+	    Numbers[i] |= (ch << (8 * j));
+	}
+	/* "unsigned" and NCURSES_INT2 are the same size - no sign-extension */
+	TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
+    }
+    return size;
+}
+#else
+static size_t
+convert_32bits(char *buf, NCURSES_INT2 *Numbers, int count)
+{
+    int i, j;
+    unsigned char ch;
+    for (i = 0; i < count; i++) {
+	int value = 0;
+	for (j = 0; j < SIZEOF_32BITS; ++j) {
+	    ch = UChar(*buf++);
+	    value |= (ch << (8 * j));
+	}
+	if (value == -1)
+	    Numbers[i] = ABSENT_NUMERIC;
+	else if (value == -2)
+	    Numbers[i] = CANCELLED_NUMERIC;
+	else if (value > MAX_OF_TYPE(NCURSES_INT2))
+	    Numbers[i] = MAX_OF_TYPE(NCURSES_INT2);
+	else
+	    Numbers[i] = (short) value;
+	TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
+    }
+    return SIZEOF_SHORT;
+}
+
+static size_t
+convert_16bits(char *buf, NCURSES_INT2 *Numbers, int count)
 {
     int i;
     for (i = 0; i < count; i++) {
@@ -61,13 +134,17 @@
 	    Numbers[i] = MyNumber(buf + 2 * i);
 	TR(TRACE_DATABASE, ("get Numbers[%d]=%d", i, Numbers[i]));
     }
+    return SIZEOF_SHORT;
 }
+#endif
 
-static void
-convert_strings(char *buf, char **Strings, int count, int size, char *table)
+static bool
+convert_strings(char *buf, char **Strings, int count, int size,
+		char *table, bool always)
 {
     int i;
     char *p;
+    bool success = TRUE;
 
     for (i = 0; i < count; i++) {
 	if (IS_NEG1(buf + 2 * i)) {
@@ -77,20 +154,44 @@
 	} else if (MyNumber(buf + 2 * i) > size) {
 	    Strings[i] = ABSENT_STRING;
 	} else {
-	    Strings[i] = (MyNumber(buf + 2 * i) + table);
-	    TR(TRACE_DATABASE, ("Strings[%d] = %s", i, _nc_visbuf(Strings[i])));
+	    int nn = MyNumber(buf + 2 * i);
+	    if (nn >= 0 && nn < size) {
+		Strings[i] = (nn + table);
+		TR(TRACE_DATABASE, ("Strings[%d] = %s", i,
+				    _nc_visbuf(Strings[i])));
+	    } else {
+		TR(TRACE_DATABASE,
+		   ("found out-of-range index %d to Strings[%d]", nn, i));
+		success = FALSE;
+		break;
+	    }
 	}
 
 	/* make sure all strings are NUL terminated */
 	if (VALID_STRING(Strings[i])) {
-	    for (p = Strings[i]; p <= table + size; p++)
+	    for (p = Strings[i]; p < table + size; p++)
 		if (*p == '\0')
 		    break;
 	    /* if there is no NUL, ignore the string */
-	    if (p > table + size)
+	    if (p >= table + size) {
 		Strings[i] = ABSENT_STRING;
+	    } else if (p == Strings[i] && always) {
+		TR(TRACE_DATABASE,
+		   ("found empty but required Strings[%d]", i));
+		success = FALSE;
+		break;
+	    }
+	} else if (always) {	/* names are always needed */
+	    TR(TRACE_DATABASE,
+	       ("found invalid but required Strings[%d]", i));
+	    success = FALSE;
+	    break;
 	}
     }
+    if (!success) {
+	_nc_warning("corrupt data found in convert_strings");
+    }
+    return success;
 }
 
 static int
@@ -112,17 +213,22 @@
 #define Read(buf, count) fake_read(buffer, &offset, limit, (char *) buf, (unsigned) count)
 
 #define read_shorts(buf, count) \
-	(Read(buf, (count)*2) == (int) (count)*2)
+	(Read(buf, (count)*SIZEOF_SHORT) == (int) (count)*SIZEOF_SHORT)
+
+#define read_numbers(buf, count) \
+	(Read(buf, (count)*(unsigned)size_of_numbers) == (int) (count)*size_of_numbers)
 
 #define even_boundary(value) \
     if ((value) % 2 != 0) Read(buf, 1)
 #endif
 
 NCURSES_EXPORT(void)
-_nc_init_termtype(TERMTYPE *const tp)
+_nc_init_termtype(TERMTYPE2 *const tp)
 {
     unsigned i;
 
+    DEBUG(2, (T_CALLED("_nc_init_termtype(tp=%p)"), (void *) tp));
+
 #if NCURSES_XNAMES
     tp->num_Booleans = BOOLCOUNT;
     tp->num_Numbers = NUMCOUNT;
@@ -134,7 +240,7 @@
     if (tp->Booleans == 0)
 	TYPE_MALLOC(NCURSES_SBOOL, BOOLCOUNT, tp->Booleans);
     if (tp->Numbers == 0)
-	TYPE_MALLOC(short, NUMCOUNT, tp->Numbers);
+	TYPE_MALLOC(NCURSES_INT2, NUMCOUNT, tp->Numbers);
     if (tp->Strings == 0)
 	TYPE_MALLOC(char *, STRCOUNT, tp->Strings);
 
@@ -146,6 +252,8 @@
 
     for_each_string(i, tp)
 	tp->Strings[i] = ABSENT_STRING;
+
+    DEBUG(2, (T_RETURN("")));
 }
 
 #if NCURSES_USE_DATABASE
@@ -169,7 +277,7 @@
  * Return TGETENT_YES if read, TGETENT_NO if not found or garbled.
  */
 NCURSES_EXPORT(int)
-_nc_read_termtype(TERMTYPE *ptr, char *buffer, int limit)
+_nc_read_termtype(TERMTYPE2 *ptr, char *buffer, int limit)
 {
     int offset = 0;
     int name_size, bool_count, num_count, str_count, str_size;
@@ -177,6 +285,13 @@
     char buf[MAX_ENTRY_SIZE + 2];
     char *string_table;
     unsigned want, have;
+    size_t (*convert_numbers) (char *, NCURSES_INT2 *, int);
+    int size_of_numbers;
+    int max_entry_size = MAX_ENTRY_SIZE;
+
+    TR(TRACE_DATABASE,
+       (T_CALLED("_nc_read_termtype(ptr=%p, buffer=%p, limit=%d)"),
+	(void *) ptr, buffer, limit));
 
     TR(TRACE_DATABASE, ("READ termtype header @%d", offset));
 
@@ -185,14 +300,33 @@
     /* grab the header */
     if (!read_shorts(buf, 6)
 	|| !IS_TIC_MAGIC(buf)) {
-	return (TGETENT_NO);
+	returnDB(TGETENT_NO);
     }
+#if NCURSES_EXT_NUMBERS
+    if (LOW_MSB(buf) == MAGIC2) {
+	convert_numbers = convert_32bits;
+	size_of_numbers = SIZEOF_INT2;
+    } else {
+	max_entry_size = MAX_ENTRY_SIZE1;
+	convert_numbers = convert_16bits;
+	size_of_numbers = SIZEOF_SHORT;
+    }
+#else
+    if (LOW_MSB(buf) == MAGIC2) {
+	convert_numbers = convert_32bits;
+	size_of_numbers = SIZEOF_32BITS;
+    } else {
+	convert_numbers = convert_16bits;
+	size_of_numbers = SIZEOF_INT2;
+    }
+#endif
 
-    name_size = MyNumber(buf + 2);
+    /* *INDENT-EQLS* */
+    name_size  = MyNumber(buf + 2);
     bool_count = MyNumber(buf + 4);
-    num_count = MyNumber(buf + 6);
-    str_count = MyNumber(buf + 8);
-    str_size = MyNumber(buf + 10);
+    num_count  = MyNumber(buf + 6);
+    str_count  = MyNumber(buf + 8);
+    str_size   = MyNumber(buf + 10);
 
     TR(TRACE_DATABASE,
        ("TERMTYPE name_size=%d, bool=%d/%d, num=%d/%d str=%d/%d(%d)",
@@ -202,26 +336,22 @@
 	|| bool_count < 0
 	|| num_count < 0
 	|| str_count < 0
+	|| bool_count > BOOLCOUNT
+	|| num_count > NUMCOUNT
+	|| str_count > STRCOUNT
 	|| str_size < 0) {
-	return (TGETENT_NO);
+	returnDB(TGETENT_NO);
     }
 
     want = (unsigned) (str_size + name_size + 1);
-    if (str_size) {
-	/* try to allocate space for the string table */
-	if (str_count * 2 >= MAX_ENTRY_SIZE
-	    || (string_table = typeMalloc(char, want)) == 0) {
-	    return (TGETENT_NO);
-	}
-    } else {
-	str_count = 0;
-	if ((string_table = typeMalloc(char, want)) == 0) {
-	    return (TGETENT_NO);
-	}
+    /* try to allocate space for the string table */
+    if (str_count * SIZEOF_SHORT >= max_entry_size
+	|| (string_table = typeMalloc(char, want)) == 0) {
+	returnDB(TGETENT_NO);
     }
 
     /* grab the name (a null-terminated string) */
-    want = min(MAX_NAME_SIZE, (unsigned) name_size);
+    want = Min(MAX_NAME_SIZE, (unsigned) name_size);
     ptr->str_table = string_table;
     ptr->term_names = string_table;
     if ((have = (unsigned) Read(ptr->term_names, want)) != want) {
@@ -234,10 +364,9 @@
 	offset = (int) (have - MAX_NAME_SIZE);
 
     /* grab the booleans */
-    if ((ptr->Booleans = TYPE_CALLOC(NCURSES_SBOOL,
-				     max(BOOLCOUNT, bool_count))) == 0
-	|| Read(ptr->Booleans, (unsigned) bool_count) < bool_count) {
-	return (TGETENT_NO);
+    TYPE_CALLOC(NCURSES_SBOOL, Max(BOOLCOUNT, bool_count), ptr->Booleans);
+    if (Read(ptr->Booleans, (unsigned) bool_count) < bool_count) {
+	returnDB(TGETENT_NO);
     }
 
     /*
@@ -249,26 +378,27 @@
     even_boundary(name_size + bool_count);
 
     /* grab the numbers */
-    if ((ptr->Numbers = TYPE_CALLOC(short, max(NUMCOUNT, num_count))) == 0
-	|| !read_shorts(buf, num_count)) {
-	return (TGETENT_NO);
+    TYPE_CALLOC(NCURSES_INT2, Max(NUMCOUNT, num_count), ptr->Numbers);
+    if (!read_numbers(buf, num_count)) {
+	returnDB(TGETENT_NO);
     }
-    convert_shorts(buf, ptr->Numbers, num_count);
+    convert_numbers(buf, ptr->Numbers, num_count);
 
-    if ((ptr->Strings = TYPE_CALLOC(char *, max(STRCOUNT, str_count))) == 0) {
-	return (TGETENT_NO);
-    }
+    TYPE_CALLOC(char *, Max(STRCOUNT, str_count), ptr->Strings);
 
     if (str_count) {
 	/* grab the string offsets */
 	if (!read_shorts(buf, str_count)) {
-	    return (TGETENT_NO);
+	    returnDB(TGETENT_NO);
 	}
 	/* finally, grab the string table itself */
 	if (Read(string_table, (unsigned) str_size) != str_size) {
-	    return (TGETENT_NO);
+	    returnDB(TGETENT_NO);
 	}
-	convert_strings(buf, ptr->Strings, str_count, str_size, string_table);
+	if (!convert_strings(buf, ptr->Strings, str_count, str_size,
+			     string_table, FALSE)) {
+	    returnDB(TGETENT_NO);
+	}
     }
 #if NCURSES_XNAMES
 
@@ -285,20 +415,20 @@
 	int ext_bool_count = MyNumber(buf + 0);
 	int ext_num_count = MyNumber(buf + 2);
 	int ext_str_count = MyNumber(buf + 4);
-	int ext_str_size = MyNumber(buf + 6);
+	int ext_str_usage = MyNumber(buf + 6);
 	int ext_str_limit = MyNumber(buf + 8);
 	unsigned need = (unsigned) (ext_bool_count + ext_num_count + ext_str_count);
 	int base = 0;
 
-	if (need >= (MAX_ENTRY_SIZE / 2)
-	    || ext_str_size >= MAX_ENTRY_SIZE
-	    || ext_str_limit >= MAX_ENTRY_SIZE
+	if ((int) need >= (max_entry_size / 2)
+	    || ext_str_usage >= max_entry_size
+	    || ext_str_limit >= max_entry_size
 	    || ext_bool_count < 0
 	    || ext_num_count < 0
 	    || ext_str_count < 0
-	    || ext_str_size < 0
+	    || ext_str_usage < 0
 	    || ext_str_limit < 0) {
-	    return (TGETENT_NO);
+	    returnDB(TGETENT_NO);
 	}
 
 	ptr->num_Booleans = UShort(BOOLCOUNT + ext_bool_count);
@@ -306,19 +436,25 @@
 	ptr->num_Strings = UShort(STRCOUNT + ext_str_count);
 
 	TYPE_REALLOC(NCURSES_SBOOL, ptr->num_Booleans, ptr->Booleans);
-	TYPE_REALLOC(short, ptr->num_Numbers, ptr->Numbers);
+	TYPE_REALLOC(NCURSES_INT2, ptr->num_Numbers, ptr->Numbers);
 	TYPE_REALLOC(char *, ptr->num_Strings, ptr->Strings);
 
-	TR(TRACE_DATABASE, ("extended header is %d/%d/%d(%d:%d)",
-			    ext_bool_count, ext_num_count, ext_str_count,
-			    ext_str_size, ext_str_limit));
+	TR(TRACE_DATABASE, ("extended header: "
+			    "bool %d, "
+			    "number %d, "
+			    "string %d(%d:%d)",
+			    ext_bool_count,
+			    ext_num_count,
+			    ext_str_count,
+			    ext_str_usage,
+			    ext_str_limit));
 
 	TR(TRACE_DATABASE, ("READ %d extended-booleans @%d",
 			    ext_bool_count, offset));
 	if ((ptr->ext_Booleans = UShort(ext_bool_count)) != 0) {
 	    if (Read(ptr->Booleans + BOOLCOUNT, (unsigned)
 		     ext_bool_count) != ext_bool_count) {
-		return (TGETENT_NO);
+		returnDB(TGETENT_NO);
 	    }
 	}
 	even_boundary(ext_bool_count);
@@ -326,20 +462,20 @@
 	TR(TRACE_DATABASE, ("READ %d extended-numbers @%d",
 			    ext_num_count, offset));
 	if ((ptr->ext_Numbers = UShort(ext_num_count)) != 0) {
-	    if (!read_shorts(buf, ext_num_count)) {
-		return (TGETENT_NO);
+	    if (!read_numbers(buf, ext_num_count)) {
+		returnDB(TGETENT_NO);
 	    }
 	    TR(TRACE_DATABASE, ("Before converting extended-numbers"));
-	    convert_shorts(buf, ptr->Numbers + NUMCOUNT, ext_num_count);
+	    convert_numbers(buf, ptr->Numbers + NUMCOUNT, ext_num_count);
 	}
 
 	TR(TRACE_DATABASE, ("READ extended-offsets @%d", offset));
-	if ((unsigned) (ext_str_count + (int) need) >= (MAX_ENTRY_SIZE / 2)) {
-	    return (TGETENT_NO);
+	if ((ext_str_count + (int) need) >= (max_entry_size / 2)) {
+	    returnDB(TGETENT_NO);
 	}
 	if ((ext_str_count || need)
 	    && !read_shorts(buf, ext_str_count + (int) need)) {
-	    return (TGETENT_NO);
+	    returnDB(TGETENT_NO);
 	}
 
 	TR(TRACE_DATABASE, ("READ %d bytes of extended-strings @%d",
@@ -348,47 +484,63 @@
 	if (ext_str_limit) {
 	    ptr->ext_str_table = typeMalloc(char, (size_t) ext_str_limit);
 	    if (ptr->ext_str_table == 0) {
-		return (TGETENT_NO);
+		returnDB(TGETENT_NO);
 	    }
 	    if (Read(ptr->ext_str_table, (unsigned) ext_str_limit) != ext_str_limit) {
-		return (TGETENT_NO);
+		returnDB(TGETENT_NO);
 	    }
 	    TR(TRACE_DATABASE, ("first extended-string is %s", _nc_visbuf(ptr->ext_str_table)));
 	}
 
 	if ((ptr->ext_Strings = UShort(ext_str_count)) != 0) {
+	    int check = (ext_bool_count + ext_num_count + ext_str_count);
+
 	    TR(TRACE_DATABASE,
-	       ("Before computing extended-string capabilities str_count=%d, ext_str_count=%d",
+	       ("Before computing extended-string capabilities "
+		"str_count=%d, ext_str_count=%d",
 		str_count, ext_str_count));
-	    convert_strings(buf, ptr->Strings + str_count, ext_str_count,
-			    ext_str_limit, ptr->ext_str_table);
+	    if (!convert_strings(buf, ptr->Strings + str_count, ext_str_count,
+				 ext_str_limit, ptr->ext_str_table, FALSE)) {
+		returnDB(TGETENT_NO);
+	    }
 	    for (i = ext_str_count - 1; i >= 0; i--) {
 		TR(TRACE_DATABASE, ("MOVE from [%d:%d] %s",
 				    i, i + str_count,
 				    _nc_visbuf(ptr->Strings[i + str_count])));
 		ptr->Strings[i + STRCOUNT] = ptr->Strings[i + str_count];
-		if (VALID_STRING(ptr->Strings[i + STRCOUNT]))
+		if (VALID_STRING(ptr->Strings[i + STRCOUNT])) {
 		    base += (int) (strlen(ptr->Strings[i + STRCOUNT]) + 1);
+		    ++check;
+		}
 		TR(TRACE_DATABASE, ("... to    [%d] %s",
 				    i + STRCOUNT,
 				    _nc_visbuf(ptr->Strings[i + STRCOUNT])));
 	    }
+	    TR(TRACE_DATABASE, ("Check table-size: %d/%d", check, ext_str_usage));
+#if 0
+	    /*
+	     * Phasing in a proper check will be done "later".
+	     */
+	    if (check != ext_str_usage)
+		returnDB(TGETENT_NO);
+#endif
 	}
 
 	if (need) {
-	    if (ext_str_count >= (MAX_ENTRY_SIZE / 2)) {
-		return (TGETENT_NO);
+	    if (ext_str_count >= (max_entry_size / 2)) {
+		returnDB(TGETENT_NO);
 	    }
-	    if ((ptr->ext_Names = TYPE_CALLOC(char *, need)) == 0) {
-		return (TGETENT_NO);
-	    }
+	    TYPE_CALLOC(char *, need, ptr->ext_Names);
 	    TR(TRACE_DATABASE,
 	       ("ext_NAMES starting @%d in extended_strings, first = %s",
 		base, _nc_visbuf(ptr->ext_str_table + base)));
-	    convert_strings(buf + (2 * ext_str_count),
-			    ptr->ext_Names,
-			    (int) need,
-			    ext_str_limit, ptr->ext_str_table + base);
+	    if (!convert_strings(buf + (2 * ext_str_count),
+				 ptr->ext_Names,
+				 (int) need,
+				 ext_str_limit, ptr->ext_str_table + base,
+				 TRUE)) {
+		returnDB(TGETENT_NO);
+	    }
 	}
 
 	TR(TRACE_DATABASE,
@@ -415,7 +567,7 @@
     for (i = str_count; i < STRCOUNT; i++)
 	ptr->Strings[i] = ABSENT_STRING;
 
-    return (TGETENT_YES);
+    returnDB(TGETENT_YES);
 }
 
 /*
@@ -427,26 +579,31 @@
  *	table.
  */
 NCURSES_EXPORT(int)
-_nc_read_file_entry(const char *const filename, TERMTYPE *ptr)
+_nc_read_file_entry(const char *const filename, TERMTYPE2 *ptr)
 /* return 1 if read, 0 if not found or garbled */
 {
     FILE *fp = 0;
     int code;
-    int limit;
-    char buffer[MAX_ENTRY_SIZE + 1];
 
     if (_nc_access(filename, R_OK) < 0
-	|| (fp = fopen(filename, "rb")) == 0) {
+	|| (fp = safe_fopen(filename, BIN_R)) == 0) {
 	TR(TRACE_DATABASE, ("cannot open terminfo %s (errno=%d)", filename, errno));
 	code = TGETENT_NO;
     } else {
-	if ((limit = (int) fread(buffer, sizeof(char), sizeof(buffer), fp))
-	    > 0) {
+	int limit;
+	char buffer[MAX_ENTRY_SIZE + 1];
+
+	limit = (int) fread(buffer, sizeof(char), sizeof(buffer), fp);
+	if (limit > 0) {
+	    const char *old_source = _nc_get_source();
 
 	    TR(TRACE_DATABASE, ("read terminfo %s", filename));
+	    if (old_source == NULL)
+		_nc_set_source(filename);
 	    if ((code = _nc_read_termtype(ptr, buffer, limit)) == TGETENT_NO) {
-		_nc_free_termtype(ptr);
+		_nc_free_termtype2(ptr);
 	    }
+	    _nc_set_source(old_source);
 	} else {
 	    code = TGETENT_NO;
 	}
@@ -508,6 +665,105 @@
     return result;
 }
 
+static int
+lookup_b64(int *target, const char **source)
+{
+    int result = 3;
+    int j;
+    /*
+     * ncurses' quickdump writes only RFC 4648 "url/filename-safe" encoding,
+     * but accepts RFC-3548
+     */
+    for (j = 0; j < 4; ++j) {
+	int ch = UChar(**source);
+	*source += 1;
+	if (ch >= 'A' && ch <= 'Z') {
+	    target[j] = (ch - 'A');
+	} else if (ch >= 'a' && ch <= 'z') {
+	    target[j] = 26 + (ch - 'a');
+	} else if (ch >= '0' && ch <= '9') {
+	    target[j] = 52 + (ch - '0');
+	} else if (ch == '-' || ch == '+') {
+	    target[j] = 62;
+	} else if (ch == '_' || ch == '/') {
+	    target[j] = 63;
+	} else if (ch == '=') {
+	    target[j] = 64;
+	    result--;
+	} else {
+	    result = -1;
+	    break;
+	}
+    }
+    return result;
+}
+
+static int
+decode_hex(const char **source)
+{
+    int result = 0;
+    int nibble;
+
+    for (nibble = 0; nibble < 2; ++nibble) {
+	int ch = UChar(**source);
+	result <<= 4;
+	*source += 1;
+	if (ch >= '0' && ch <= '9') {
+	    ch -= '0';
+	} else if (ch >= 'A' && ch <= 'F') {
+	    ch -= 'A';
+	    ch += 10;
+	} else if (ch >= 'a' && ch <= 'f') {
+	    ch -= 'a';
+	    ch += 10;
+	} else {
+	    result = -1;
+	    break;
+	}
+	result |= ch;
+    }
+    return result;
+}
+
+static int
+decode_quickdump(char *target, const char *source)
+{
+    char *base = target;
+    int result = 0;
+
+    if (!strncmp(source, "b64:", (size_t) 4)) {
+	source += 4;
+	while (*source != '\0') {
+	    int bits[4];
+	    int ch = lookup_b64(bits, &source);
+	    if (ch < 0 || (ch + target - base) >= MAX_ENTRY_SIZE) {
+		result = 0;
+		break;
+	    }
+	    result += ch;
+	    *target++ = (char) ((bits[0] << 2) | (bits[1] >> 4));
+	    if (bits[2] < 64) {
+		*target++ = (char) ((bits[1] << 4) | (bits[2] >> 2));
+		if (bits[3] < 64) {
+		    *target++ = (char) ((bits[2] << 6) | bits[3]);
+		}
+	    }
+	}
+    } else if (!strncmp(source, "hex:", (size_t) 4)) {
+	source += 4;
+	while (*source != '\0') {
+	    int ch = decode_hex(&source);
+	    if (ch < 0 || (target - base) >= MAX_ENTRY_SIZE) {
+		result = 0;
+		break;
+	    }
+	    *target++ = (char) ch;
+	    ++result;
+	}
+    }
+    return result;
+}
+
 /*
  * Build a terminfo pathname and try to read the data.  Returns TGETENT_YES on
  * success, TGETENT_NO on failure.
@@ -517,20 +773,39 @@
 		   unsigned limit,
 		   const char *const path,
 		   const char *name,
-		   TERMTYPE *const tp)
+		   TERMTYPE2 *const tp)
 {
     int code = TGETENT_NO;
-
 #if USE_HASHED_DB
     DB *capdbp;
+#endif
+    char buffer[MAX_ENTRY_SIZE + 1];
+    int used;
 
-    if (make_db_filename(filename, limit, path)
-	&& (capdbp = _nc_db_open(filename, FALSE)) != 0) {
+    TR(TRACE_DATABASE,
+       (T_CALLED("_nc_read_tic_entry(file=%p, path=%s, name=%s)"),
+	filename, path, name));
+
+    assert(TGETENT_YES == TRUE);	/* simplify call for _nc_name_match */
+
+    if ((used = decode_quickdump(buffer, path)) != 0
+	&& (code = _nc_read_termtype(tp, buffer, used)) == TGETENT_YES
+	&& (code = _nc_name_match(tp->term_names, name, "|")) == TGETENT_YES) {
+	TR(TRACE_DATABASE, ("loaded quick-dump for %s", name));
+	/* shorten name shown by infocmp */
+	_nc_STRCPY(filename, "$TERMINFO", limit);
+    } else
+#if USE_HASHED_DB
+	if (make_db_filename(filename, limit, path)
+	    && (capdbp = _nc_db_open(filename, FALSE)) != 0) {
 
 	DBT key, data;
 	int reccnt = 0;
 	char *save = strdup(name);
 
+	if (save == 0)
+	    returnDB(code);
+
 	memset(&key, 0, sizeof(key));
 	key.data = save;
 	key.size = strlen(save);
@@ -540,7 +815,7 @@
 	 * looking for compiled (binary) terminfo data.
 	 *
 	 * cgetent uses a two-level lookup.  On the first it uses the given
-	 * name to return a record containing only the aliases for an entry. 
+	 * name to return a record containing only the aliases for an entry.
 	 * On the second (using that list of aliases as a key), it returns the
 	 * content of the terminal description.  We expect second lookup to
 	 * return data beginning with the same set of aliases.
@@ -551,15 +826,15 @@
 	 * (source/binary) by checking the lengths.
 	 */
 	while (_nc_db_get(capdbp, &key, &data) == 0) {
-	    int used = (int) data.size - 1;
 	    char *have = (char *) data.data;
+	    used = (int) data.size - 1;
 
 	    if (*have++ == 0) {
 		if (data.size > key.size
 		    && IS_TIC_MAGIC(have)) {
 		    code = _nc_read_termtype(tp, have, used);
 		    if (code == TGETENT_NO) {
-			_nc_free_termtype(tp);
+			_nc_free_termtype2(tp);
 		    }
 		}
 		break;
@@ -586,30 +861,29 @@
 	code = _nc_read_file_entry(filename, tp);
     }
 #if NCURSES_USE_TERMCAP
-    else if (code != TGETENT_YES) {
+    if (code != TGETENT_YES) {
 	code = _nc_read_termcap_entry(name, tp);
 	_nc_SPRINTF(filename, _nc_SLIMIT(PATH_MAX)
 		    "%.*s", PATH_MAX - 1, _nc_get_source());
     }
 #endif
-    return code;
+    returnDB(code);
 }
 #endif /* NCURSES_USE_DATABASE */
 
 /*
- *	_nc_read_entry(char *name, char *filename, TERMTYPE *tp)
- *
- *	Find and read the compiled entry for a given terminal type,
- *	if it exists.  We take pains here to make sure no combination
- *	of environment variables and terminal type name can be used to
- *	overrun the file buffer.
+ * Find and read the compiled entry for a given terminal type, if it exists.
+ * We take pains here to make sure no combination of environment variables and
+ * terminal type name can be used to overrun the file buffer.
  */
-
 NCURSES_EXPORT(int)
-_nc_read_entry(const char *const name, char *const filename, TERMTYPE *const tp)
+_nc_read_entry2(const char *const name, char *const filename, TERMTYPE2 *const tp)
 {
     int code = TGETENT_NO;
 
+    if (name == 0)
+	return _nc_read_entry2("", filename, tp);
+
     _nc_SPRINTF(filename, _nc_SLIMIT(PATH_MAX)
 		"%.*s", PATH_MAX - 1, name);
 
@@ -628,7 +902,6 @@
 	_nc_first_db(&state, &offset);
 	code = TGETENT_ERR;
 	while ((path = _nc_next_db(&state, &offset)) != 0) {
-	    TR(TRACE_DATABASE, ("_nc_read_tic_entry path=%s, name=%s", path, name));
 	    code = _nc_read_tic_entry(filename, PATH_MAX, path, name, tp);
 	    if (code == TGETENT_YES) {
 		_nc_last_db();
@@ -645,3 +918,16 @@
     }
     return code;
 }
+
+#if NCURSES_EXT_NUMBERS
+NCURSES_EXPORT(int)
+_nc_read_entry(const char *const name, char *const filename, TERMTYPE *const tp)
+{
+    TERMTYPE2 dummy;
+    int rc;
+    rc = _nc_read_entry2(name, filename, &dummy);
+    if (rc == TGETENT_YES)
+	_nc_export_termtype2(tp, &dummy);
+    return rc;
+}
+#endif
diff --git a/ncurses/tinfo/read_termcap.c b/ncurses/tinfo/read_termcap.c
index 6bfb23c..1a29484 100644
--- a/ncurses/tinfo/read_termcap.c
+++ b/ncurses/tinfo/read_termcap.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
+ * Copyright 2018-2021,2023 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            *
@@ -56,7 +57,7 @@
 #include <sys/types.h>
 #include <tic.h>
 
-MODULE_ID("$Id: read_termcap.c,v 1.89 2013/12/15 00:32:43 tom Exp $")
+MODULE_ID("$Id: read_termcap.c,v 1.104 2023/06/24 21:53:16 tom Exp $")
 
 #if !PURE_TERMINFO
 
@@ -66,10 +67,10 @@
 #define TC_REF_LOOP   -3
 #define TC_UNRESOLVED -4	/* this is not returned by BSD cgetent */
 
-static NCURSES_CONST char *
+static const char *
 get_termpath(void)
 {
-    NCURSES_CONST char *result;
+    const char *result;
 
     if (!use_terminfo_vars() || (result = getenv("TERMPATH")) == 0)
 	result = TERMPATH;
@@ -186,7 +187,7 @@
     bp = buf;
     for (;;) {
 	/*
-	 * Skip past the current capability field - it's either the
+	 * Skip past the current capability field - it is either the
 	 * name field if this is the first time through the loop, or
 	 * the remainder of a field whose name failed to match cap.
 	 */
@@ -323,7 +324,7 @@
 	    if (fd >= 0) {
 		(void) lseek(fd, (off_t) 0, SEEK_SET);
 	    } else if ((_nc_access(db_array[current], R_OK) < 0)
-		       || (fd = open(db_array[current], O_RDONLY, 0)) < 0) {
+		       || (fd = safe_open2(db_array[current], O_RDONLY)) < 0) {
 		/* No error on unfound file. */
 		if (errno == ENOENT)
 		    continue;
@@ -364,7 +365,7 @@
 			if (bp >= b_end) {
 			    int n;
 
-			    n = read(fd, buf, sizeof(buf));
+			    n = (int) read(fd, buf, sizeof(buf));
 			    if (n <= 0) {
 				if (myfd)
 				    (void) close(fd);
@@ -393,7 +394,7 @@
 				|| *(rp - 1) != '\\')
 				break;
 			}
-			*rp++ = c;
+			*rp++ = (char) c;
 
 			/*
 			 * Enforce loop invariant: if no room
@@ -404,8 +405,8 @@
 			    unsigned pos;
 			    size_t newsize;
 
-			    pos = rp - record;
-			    newsize = r_end - record + BFRAG;
+			    pos = (unsigned) (rp - record);
+			    newsize = (size_t) (r_end - record + BFRAG);
 			    record = DOALLOC(newsize);
 			    if (record == 0) {
 				if (myfd)
@@ -492,14 +493,14 @@
 		}
 	    }
 	    tcstart = tc - 3;
-	    tclen = s - tcstart;
+	    tclen = (int) (s - tcstart);
 	    tcend = s;
 
 	    icap = 0;
 	    iret = _nc_getent(&icap, &ilen, &oline, current, db_array, fd,
 			      tc, depth + 1, 0);
 	    newicap = icap;	/* Put into a register. */
-	    newilen = ilen;
+	    newilen = (int) ilen;
 	    if (iret != TC_SUCCESS) {
 		/* an error */
 		if (iret < TC_NOT_FOUND) {
@@ -523,7 +524,7 @@
 	    /* not interested in name field of tc'ed record */
 	    s = newicap;
 	    while (*s != '\0' && *s++ != ':') ;
-	    newilen -= s - newicap;
+	    newilen -= (int) (s - newicap);
 	    newicap = s;
 
 	    /* make sure interpolated record is `:'-terminated */
@@ -542,10 +543,10 @@
 		unsigned pos, tcpos, tcposend;
 		size_t newsize;
 
-		pos = rp - record;
-		newsize = r_end - record + diff + BFRAG;
-		tcpos = tcstart - record;
-		tcposend = tcend - record;
+		pos = (unsigned) (rp - record);
+		newsize = (size_t) (r_end - record + diff + BFRAG);
+		tcpos = (unsigned) (tcstart - record);
+		tcposend = (unsigned) (tcend - record);
 		record = DOALLOC(newsize);
 		if (record == 0) {
 		    if (myfd)
@@ -583,7 +584,7 @@
      */
     if (myfd)
 	(void) close(fd);
-    *len = rp - record - 1;	/* don't count NUL */
+    *len = (unsigned) (rp - record - 1);	/* don't count NUL */
     if (r_end > rp) {
 	if ((record = DOALLOC((size_t) (rp - record))) == 0) {
 	    errno = ENOMEM;
@@ -791,7 +792,7 @@
     int i;
     char pathbuf[PBUFSIZ];	/* holds raw path of filenames */
     CGETENT_CONST char *pathvec[PVECSIZ];	/* point to names in pathbuf */
-    NCURSES_CONST char *termpath;
+    const char *termpath;
     string_desc desc;
 
     *lineno = 1;
@@ -802,7 +803,7 @@
     /*
      * TERMCAP can have one of two things in it.  It can be the name of a file
      * to use instead of /etc/termcap.  In this case it better start with a
-     * "/".  Or it can be an entry to use so we don't have to read the file. 
+     * "/".  Or it can be an entry to use so we don't have to read the file.
      * In this case it has to already have the newlines crunched out.  If
      * TERMCAP does not hold a file name then a path of names is searched
      * instead.  The path is found in the TERMPATH variable, or becomes
@@ -956,7 +957,7 @@
 #endif /* !USE_GETCAP */
 
 NCURSES_EXPORT(int)
-_nc_read_termcap_entry(const char *const tn, TERMTYPE *const tp)
+_nc_read_termcap_entry(const char *const tn, TERMTYPE2 *const tp)
 {
     int found = TGETENT_NO;
     ENTRY *ep;
@@ -965,6 +966,8 @@
 #endif
 #if USE_GETCAP
     char *p, tc[TBUFSIZ];
+    char *tc_buf = 0;
+#define MY_SIZE sizeof(tc) - 1
     int status;
     static char *source;
     static int lineno;
@@ -982,8 +985,7 @@
     if (use_terminfo_vars() && (p = getenv("TERMCAP")) != 0
 	&& !_nc_is_abs_path(p) && _nc_name_match(p, tn, "|:")) {
 	/* TERMCAP holds a termcap entry */
-	strncpy(tc, p, sizeof(tc) - 1);
-	tc[sizeof(tc) - 1] = '\0';
+	tc_buf = strdup(p);
 	_nc_set_source("TERMCAP");
     } else {
 	/* we're using getcap(3) */
@@ -992,8 +994,13 @@
 
 	_nc_curr_line = lineno;
 	_nc_set_source(source);
+	tc_buf = tc;
     }
-    _nc_read_entry_source((FILE *) 0, tc, FALSE, TRUE, NULLHOOK);
+    if (tc_buf == 0)
+	return (TGETENT_ERR);
+    _nc_read_entry_source((FILE *) 0, tc_buf, FALSE, TRUE, NULLHOOK);
+    if (tc_buf != tc)
+	free(tc_buf);
 #else
     /*
      * Here is what the 4.4BSD termcap(3) page prescribes:
@@ -1027,7 +1034,7 @@
     int j, k;
     bool use_buffer = FALSE;
     bool normal = TRUE;
-    char tc_buf[1024];
+    char *tc_buf = 0;
     char pathbuf[PATH_MAX];
     char *copied = 0;
     char *cp;
@@ -1039,10 +1046,8 @@
 	    ADD_TC(tc, 0);
 	    normal = FALSE;
 	} else if (_nc_name_match(tc, tn, "|:")) {	/* treat as a capability file */
-	    use_buffer = TRUE;
-	    _nc_SPRINTF(tc_buf,
-			_nc_SLIMIT(sizeof(tc_buf))
-			"%.*s\n", (int) sizeof(tc_buf) - 2, tc);
+	    tc_buf = strdup(tc);
+	    use_buffer = (tc_buf != 0);
 	    normal = FALSE;
 	}
     }
@@ -1050,23 +1055,25 @@
     if (normal) {		/* normal case */
 	char envhome[PATH_MAX], *h;
 
-	copied = strdup(get_termpath());
-	for (cp = copied; *cp; cp++) {
-	    if (*cp == NCURSES_PATHSEP)
-		*cp = '\0';
-	    else if (cp == copied || cp[-1] == '\0') {
-		ADD_TC(cp, filecount);
+	if ((copied = strdup(get_termpath())) != 0) {
+	    for (cp = copied; *cp; cp++) {
+		if (*cp == NCURSES_PATHSEP)
+		    *cp = '\0';
+		else if (cp == copied || cp[-1] == '\0') {
+		    ADD_TC(cp, filecount);
+		}
 	    }
 	}
-
-#define PRIVATE_CAP "%s/.termcap"
+#define PRIVATE_CAP "%.*s/.termcap"
 
 	if (use_terminfo_vars() && (h = getenv("HOME")) != NULL && *h != '\0'
 	    && (strlen(h) + sizeof(PRIVATE_CAP)) < PATH_MAX) {
 	    /* user's .termcap, if any, should override it */
 	    _nc_STRCPY(envhome, h, sizeof(envhome));
 	    _nc_SPRINTF(pathbuf, _nc_SLIMIT(sizeof(pathbuf))
-			PRIVATE_CAP, envhome);
+			PRIVATE_CAP,
+			(int) (sizeof(pathbuf) - sizeof(PRIVATE_CAP)),
+			envhome);
 	    ADD_TC(pathbuf, filecount);
 	}
     }
@@ -1108,9 +1115,10 @@
 
 	/*
 	 * We don't suppress warning messages here.  The presumption is
-	 * that since it's just a single entry, they won't be a pain.
+	 * that since it is just a single entry, they won't be a pain.
 	 */
 	_nc_read_entry_source((FILE *) 0, tc_buf, FALSE, FALSE, NULLHOOK);
+	free(tc_buf);
     } else {
 	int i;
 
@@ -1118,7 +1126,7 @@
 
 	    TR(TRACE_DATABASE, ("Looking for %s in %s", tn, termpaths[i]));
 	    if (_nc_access(termpaths[i], R_OK) == 0
-		&& (fp = fopen(termpaths[i], "r")) != (FILE *) 0) {
+		&& (fp = safe_fopen(termpaths[i], "r")) != (FILE *) 0) {
 		_nc_set_source(termpaths[i]);
 
 		/*
@@ -1140,7 +1148,8 @@
 	return (TGETENT_ERR);
 
     /* resolve all use references */
-    _nc_resolve_uses2(TRUE, FALSE);
+    if (_nc_resolve_uses2(TRUE, FALSE) != TRUE)
+	return (TGETENT_ERR);
 
     /* find a terminal matching tn, if we can */
 #if USE_GETCAP_CACHE
@@ -1157,7 +1166,7 @@
 		_nc_free_entry(_nc_head, &(ep->tterm));
 
 		/*
-		 * OK, now try to write the type to user's terminfo directory. 
+		 * OK, now try to write the type to user's terminfo directory.
 		 * Next time he loads this, it will come through terminfo.
 		 *
 		 * Advantage:  Second and subsequent fetches of this entry will
diff --git a/ncurses/tinfo/strings.c b/ncurses/tinfo/strings.c
index 393d8e7..03f59c2 100644
--- a/ncurses/tinfo/strings.c
+++ b/ncurses/tinfo/strings.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2000-2007,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020,2023 Thomas E. Dickey                                     *
+ * Copyright 2000-2012,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            *
@@ -35,8 +36,9 @@
 **/
 
 #include <curses.priv.h>
+#include <tic.h>
 
-MODULE_ID("$Id: strings.c,v 1.8 2012/02/22 22:34:31 tom Exp $")
+MODULE_ID("$Id: strings.c,v 1.11 2023/05/27 20:13:10 tom Exp $")
 
 /****************************************************************************
  * Useful string functions (especially for mvcur)
@@ -93,7 +95,7 @@
  * Copy a descriptor
  */
 NCURSES_EXPORT(string_desc *)
-_nc_str_copy(string_desc * dst, string_desc * src)
+_nc_str_copy(string_desc * dst, const string_desc * const src)
 {
     *dst = *src;
     return dst;
@@ -105,7 +107,7 @@
 NCURSES_EXPORT(bool)
 _nc_safe_strcat(string_desc * dst, const char *src)
 {
-    if (src != 0) {
+    if (PRESENT(src)) {
 	size_t len = strlen(src);
 
 	if (len < dst->s_size) {
@@ -126,7 +128,7 @@
 NCURSES_EXPORT(bool)
 _nc_safe_strcpy(string_desc * dst, const char *src)
 {
-    if (src != 0) {
+    if (PRESENT(src)) {
 	size_t len = strlen(src);
 
 	if (len < dst->s_size) {
diff --git a/ncurses/tinfo/tinfo_driver.c b/ncurses/tinfo/tinfo_driver.c
index c6a1c22..fd993b8 100644
--- a/ncurses/tinfo/tinfo_driver.c
+++ b/ncurses/tinfo/tinfo_driver.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2008-2014,2015 Free Software Foundation, Inc.              *
+ * Copyright 2018-2022,2023 Thomas E. Dickey                                *
+ * Copyright 2008-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            *
@@ -28,12 +29,13 @@
 
 /****************************************************************************
  *  Author: Juergen Pfeifer                                                 *
- *                                                                          *
+ *     and: Thomas E. Dickey                                                *
  ****************************************************************************/
 
 #include <curses.priv.h>
-#define CUR ((TERMINAL*)TCB)->type.
+#define CUR TerminalType((TERMINAL*)TCB).
 #include <tic.h>
+#include <termcap.h>		/* ospeed */
 
 #if HAVE_NANOSLEEP
 #include <time.h>
@@ -50,7 +52,7 @@
 # endif
 #endif
 
-MODULE_ID("$Id: tinfo_driver.c,v 1.40 2015/06/27 01:20:41 tom Exp $")
+MODULE_ID("$Id: tinfo_driver.c,v 1.74 2023/09/16 10:44:33 tom Exp $")
 
 /*
  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
@@ -113,9 +115,43 @@
     return "tinfo";
 }
 
+static void
+get_baudrate(TERMINAL *termp)
+{
+    int my_ospeed;
+    int result;
+    if (GET_TTY(termp->Filedes, &termp->Nttyb) == OK) {
+#ifdef TERMIOS
+	termp->Nttyb.c_oflag &= (unsigned) (~OFLAGS_TABS);
+#elif defined(EXP_WIN32_DRIVER)
+	/* noop */
+#else
+	termp->Nttyb.sg_flags &= (unsigned) (~XTABS);
+#endif
+    }
+#ifdef USE_OLD_TTY
+    result = (int) cfgetospeed(&(termp->Nttyb));
+    my_ospeed = (NCURSES_OSPEED) _nc_ospeed(result);
+#else /* !USE_OLD_TTY */
+#ifdef TERMIOS
+    my_ospeed = (NCURSES_OSPEED) cfgetospeed(&(termp->Nttyb));
+#elif defined(EXP_WIN32_DRIVER)
+    /* noop */
+    my_ospeed = 0;
+#else
+    my_ospeed = (NCURSES_OSPEED) termp->Nttyb.sg_ospeed;
+#endif
+    result = _nc_baudrate(my_ospeed);
+#endif
+    termp->_baudrate = result;
+    ospeed = (NCURSES_OSPEED) my_ospeed;
+}
+
 #undef SETUP_FAIL
 #define SETUP_FAIL FALSE
 
+#define NO_COPY {}
+
 static bool
 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, const char *tname, int *errret)
 {
@@ -125,7 +161,7 @@
     SCREEN *sp;
 
     START_TRACE();
-    T((T_CALLED("tinfo::drv_CanHandle(%p)"), TCB));
+    T((T_CALLED("tinfo::drv_CanHandle(%p)"), (void *) TCB));
 
     assert(TCB != 0 && tname != 0);
     termp = (TERMINAL *) TCB;
@@ -133,17 +169,20 @@
     TCB->magic = TCBMAGIC;
 
 #if (NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP)
-    status = _nc_setup_tinfo(tname, &termp->type);
+    status = _nc_setup_tinfo(tname, &TerminalType(termp));
+    T(("_nc_setup_tinfo returns %d", status));
 #else
+    T(("no database available"));
     status = TGETENT_NO;
 #endif
 
     /* try fallback list if entry on disk */
     if (status != TGETENT_YES) {
-	const TERMTYPE *fallback = _nc_fallback(tname);
+	const TERMTYPE2 *fallback = _nc_fallback2(tname);
 
 	if (fallback) {
-	    termp->type = *fallback;
+	    T(("found fallback entry"));
+	    TerminalType(termp) = *fallback;
 	    status = TGETENT_YES;
 	}
     }
@@ -153,17 +192,40 @@
 	if (status == TGETENT_ERR) {
 	    ret_error0(status, "terminals database is inaccessible\n");
 	} else if (status == TGETENT_NO) {
-	    ret_error1(status, "unknown terminal type.\n", tname);
+	    ret_error1(status, "unknown terminal type.\n",
+		       tname, NO_COPY);
+	} else {
+	    ret_error0(status, "unexpected return-code\n");
 	}
     }
     result = TRUE;
+#if NCURSES_EXT_NUMBERS
+    _nc_export_termtype2(&termp->type, &TerminalType(termp));
+#endif
 #if !USE_REENTRANT
-    strncpy(ttytype, termp->type.term_names, (size_t) NAMESIZE - 1);
-    ttytype[NAMESIZE - 1] = '\0';
+    save_ttytype(termp);
 #endif
 
-    if (command_character)
-	_nc_tinfo_cmdch(termp, *command_character);
+    if (VALID_STRING(command_character))
+	_nc_tinfo_cmdch(termp, UChar(*command_character));
+
+    /*
+     * If an application calls setupterm() rather than initscr() or
+     * newterm(), we will not have the def_prog_mode() call in
+     * _nc_setupscreen().  Do it now anyway, so we can initialize the
+     * baudrate.
+     */
+    if (sp == 0 && NC_ISATTY(termp->Filedes)) {
+	get_baudrate(termp);
+    }
+#if NCURSES_EXT_NUMBERS
+#define cleanup_termtype() \
+    _nc_free_termtype2(&TerminalType(termp)); \
+    _nc_free_termtype(&termp->type)
+#else
+#define cleanup_termtype() \
+    _nc_free_termtype2(&TerminalType(termp))
+#endif
 
     if (generic_type) {
 	/*
@@ -173,13 +235,19 @@
 	if ((VALID_STRING(cursor_address)
 	     || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home)))
 	    && VALID_STRING(clear_screen)) {
-	    ret_error1(TGETENT_YES, "terminal is not really generic.\n", tname);
+	    cleanup_termtype();
+	    ret_error1(TGETENT_YES, "terminal is not really generic.\n",
+		       tname, NO_COPY);
 	} else {
-	    ret_error1(TGETENT_NO, "I need something more specific.\n", tname);
+	    cleanup_termtype();
+	    ret_error1(TGETENT_NO, "I need something more specific.\n",
+		       tname, NO_COPY);
 	}
     }
     if (hard_copy) {
-	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n", tname);
+	cleanup_termtype();
+	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n",
+		   tname, NO_COPY);
     }
 
     returnBool(result);
@@ -261,8 +329,8 @@
 	sp->_has_sgr_39_49 = (NCURSES_SP_NAME(tigetflag) (NCURSES_SP_ARGx
 							  "AX")
 			      == TRUE);
-	sp->_default_fg = isDefaultColor(fg) ? COLOR_DEFAULT : (fg & C_MASK);
-	sp->_default_bg = isDefaultColor(bg) ? COLOR_DEFAULT : (bg & C_MASK);
+	sp->_default_fg = isDefaultColor(fg) ? COLOR_DEFAULT : fg;
+	sp->_default_bg = isDefaultColor(bg) ? COLOR_DEFAULT : bg;
 	if (sp->_color_pairs != 0) {
 	    bool save = sp->_default_color;
 	    sp->_default_color = TRUE;
@@ -293,23 +361,23 @@
 	if (set_a_foreground) {
 	    TPUTS_TRACE("set_a_foreground");
 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
-				    TPARM_1(set_a_foreground, color), 1, outc);
+				    TIPARM_1(set_a_foreground, color), 1, outc);
 	} else {
 	    TPUTS_TRACE("set_foreground");
 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
-				    TPARM_1(set_foreground,
-					    toggled_colors(color)), 1, outc);
+				    TIPARM_1(set_foreground,
+					     toggled_colors(color)), 1, outc);
 	}
     } else {
 	if (set_a_background) {
 	    TPUTS_TRACE("set_a_background");
 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
-				    TPARM_1(set_a_background, color), 1, outc);
+				    TIPARM_1(set_a_background, color), 1, outc);
 	} else {
 	    TPUTS_TRACE("set_background");
 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
-				    TPARM_1(set_background,
-					    toggled_colors(color)), 1, outc);
+				    TIPARM_1(set_background,
+					     toggled_colors(color)), 1, outc);
 	}
     }
 }
@@ -358,18 +426,25 @@
 
     if (sp) {
 	useEnv = sp->_use_env;
-	useTioctl = sp->_use_tioctl;
+	useTioctl = sp->use_tioctl;
     } else {
 	useEnv = _nc_prescreen.use_env;
 	useTioctl = _nc_prescreen.use_tioctl;
     }
 
+#ifdef EXP_WIN32_DRIVER
+    /* If we are here, then Windows console is used in terminfo mode.
+       We need to figure out the size using the console API
+     */
+    _nc_console_size(linep, colp);
+    T(("screen size: winconsole lines = %d columns = %d", *linep, *colp));
+#else
     /* figure out the size of the screen */
     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
 
     *linep = (int) lines;
     *colp = (int) columns;
-
+#endif
     if (useEnv || useTioctl) {
 	int value;
 
@@ -539,6 +614,8 @@
 	    if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
 #ifdef TERMIOS
 		_term->Nttyb.c_oflag &= (unsigned) ~OFLAGS_TABS;
+#elif defined(EXP_WIN32_DRIVER)
+		/* noop */
 #else
 		_term->Nttyb.sg_flags &= (unsigned) ~XTABS;
 #endif
@@ -564,6 +641,8 @@
 #ifdef TERMIOS
 		if (_term->Ottyb.c_oflag & OFLAGS_TABS)
 		    tab = back_tab = NULL;
+#elif defined(EXP_WIN32_DRIVER)
+		/* noop */
 #else
 		if (_term->Ottyb.sg_flags & XTABS)
 		    tab = back_tab = NULL;
@@ -701,10 +780,10 @@
 	    tp[b].red, tp[b].green, tp[b].blue));
 
 	NCURSES_PUTP2("initialize_pair",
-		      TPARM_7(initialize_pair,
-			      pair,
-			      tp[f].red, tp[f].green, tp[f].blue,
-			      tp[b].red, tp[b].green, tp[b].blue));
+		      TIPARM_7(initialize_pair,
+			       pair,
+			       tp[f].red, tp[f].green, tp[f].blue,
+			       tp[b].red, tp[b].green, tp[b].blue));
     }
 }
 
@@ -737,7 +816,7 @@
     AssertTCB();
     if (initialize_color != NULL) {
 	NCURSES_PUTP2("initialize_color",
-		      TPARM_4(initialize_color, color, r, g, b));
+		      TIPARM_4(initialize_color, color, r, g, b));
     }
 }
 
@@ -749,9 +828,9 @@
 	     NCURSES_SP_OUTC outc)
 {
     SCREEN *sp = TCB->csp;
-    NCURSES_COLOR_T fg = COLOR_DEFAULT;
-    NCURSES_COLOR_T bg = COLOR_DEFAULT;
-    NCURSES_COLOR_T old_fg, old_bg;
+    int fg = COLOR_DEFAULT;
+    int bg = COLOR_DEFAULT;
+    int old_fg, old_bg;
 
     AssertTCB();
     if (sp == 0)
@@ -763,22 +842,16 @@
 	if (set_color_pair) {
 	    TPUTS_TRACE("set_color_pair");
 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
-				    TPARM_1(set_color_pair, pair), 1, outc);
+				    TIPARM_1(set_color_pair, pair), 1, outc);
 	    return;
 	} else if (sp != 0) {
-	    NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx
-					   (short) pair,
-					   &fg,
-					   &bg);
+	    _nc_pair_content(SP_PARM, pair, &fg, &bg);
 	}
     }
 
     if (old_pair >= 0
 	&& sp != 0
-	&& NCURSES_SP_NAME(pair_content) (NCURSES_SP_ARGx
-					  (short) old_pair,
-					  &old_fg,
-					  &old_bg) !=ERR) {
+	&& _nc_pair_content(SP_PARM, old_pair, &old_fg, &old_bg) != ERR) {
 	if ((isDefaultColor(fg) && !isDefaultColor(old_fg))
 	    || (isDefaultColor(bg) && !isDefaultColor(old_bg))) {
 #if NCURSES_EXT_FUNCS
@@ -807,13 +880,13 @@
 
 #if NCURSES_EXT_FUNCS
     if (isDefaultColor(fg))
-	fg = (NCURSES_COLOR_T) default_fg(sp);
+	fg = default_fg(sp);
     if (isDefaultColor(bg))
-	bg = (NCURSES_COLOR_T) default_bg(sp);
+	bg = default_bg(sp);
 #endif
 
     if (reverse) {
-	NCURSES_COLOR_T xx = fg;
+	int xx = fg;
 	fg = bg;
 	bg = xx;
     }
@@ -849,12 +922,9 @@
 
     /* we know how to recognize mouse events under "xterm" */
     if (sp != 0) {
-	if (key_mouse != 0) {
-	    if (!strcmp(key_mouse, xterm_kmous)
-		|| strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) {
-		init_xterm_mouse(sp);
-	    }
-	} else if (strstr(TerminalOf(sp)->type.term_names, "xterm") != 0) {
+	if (NonEmpty(key_mouse)) {
+	    init_xterm_mouse(sp);
+	} else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) {
 	    if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
 		init_xterm_mouse(sp);
 	}
@@ -879,11 +949,18 @@
     } else
 #endif
     {
+#ifdef EXP_WIN32_DRIVER
+	rc = _nc_console_testmouse(sp,
+				   _nc_console_handle(sp->_ifd),
+				   delay
+				   EVENTLIST_2nd(evl));
+#else
 	rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
 				      TWAIT_MASK,
 				      delay,
 				      (int *) 0
 				      EVENTLIST_2nd(evl));
+#endif
 #if USE_SYSMOUSE
 	if ((sp->_mouse_type == M_SYSMOUSE)
 	    && (sp->_sysmouse_head < sp->_sysmouse_tail)
@@ -979,12 +1056,18 @@
 {
     AssertTCB();
 
-    clear_screen = 0;
-    cursor_down = parm_down_cursor = 0;
-    cursor_address = 0;
-    cursor_up = parm_up_cursor = 0;
-    row_address = 0;
-    cursor_home = carriage_return;
+    /* *INDENT-EQLS* */
+    clear_screen     = ABSENT_STRING;
+    cursor_address   = ABSENT_STRING;
+    cursor_down      = ABSENT_STRING;
+    cursor_up        = ABSENT_STRING;
+    parm_down_cursor = ABSENT_STRING;
+    parm_up_cursor   = ABSENT_STRING;
+    row_address      = ABSENT_STRING;
+    cursor_home      = carriage_return;
+
+    if (back_color_erase)
+	clr_eos = ABSENT_STRING;
 }
 
 static void
@@ -1031,8 +1114,13 @@
 	while (i + 1 < length) {
 	    if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) {
 		real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET;
-		if (sp != 0)
+		T(("#%d real_map[%s] = %s",
+		   (int) i,
+		   _tracechar(UChar(acs_chars[i])),
+		   _tracechtype(real_map[UChar(acs_chars[i])])));
+		if (sp != 0) {
 		    sp->_screen_acs_map[UChar(acs_chars[i])] = TRUE;
+		}
 	    }
 	    i += 2;
 	}
@@ -1062,7 +1150,6 @@
 		   ? "DIFF"
 		   : "SAME"),
 		_nc_visbuf(show));
-
 	_nc_unlock_global(tracef);
     }
 #endif /* TRACE */
@@ -1171,30 +1258,42 @@
 
     AssertTCB();
     SetSP();
-
+#ifdef EXP_WIN32_DRIVER
+    return _nc_console_twait(sp,
+			     _nc_console_handle(sp->_ifd),
+			     mode,
+			     milliseconds,
+			     timeleft EVENTLIST_2nd(evl));
+#else
     return _nc_timed_wait(sp, mode, milliseconds, timeleft EVENTLIST_2nd(evl));
+#endif
 }
 
 static int
 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
 {
     SCREEN *sp;
-    unsigned char c2 = 0;
     int n;
+#ifndef EXP_WIN32_DRIVER
+    unsigned char c2 = 0;
+#endif
 
     AssertTCB();
     assert(buf);
     SetSP();
 
-# if USE_PTHREADS_EINTR
-    if ((pthread_self) && (pthread_kill) && (pthread_equal))
-	_nc_globals.read_thread = pthread_self();
-# endif
+    _nc_set_read_thread(TRUE);
+#ifdef EXP_WIN32_DRIVER
+    n = _nc_console_read(sp,
+			 _nc_console_handle(sp->_ifd),
+			 buf);
+#else
     n = (int) read(sp->_ifd, &c2, (size_t) 1);
-#if USE_PTHREADS_EINTR
-    _nc_globals.read_thread = 0;
 #endif
+    _nc_set_read_thread(FALSE);
+#ifndef EXP_WIN32_DRIVER
     *buf = (int) c2;
+#endif
     return n;
 }
 
@@ -1211,6 +1310,8 @@
 	    request = remaining;
 	}
     }
+#elif defined(EXP_WIN32_DRIVER)
+    Sleep((DWORD) ms);
 #else
     _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0));
 #endif
@@ -1279,23 +1380,29 @@
 	unsigned ch = (unsigned) c;
 	if (flag) {
 	    while ((s = _nc_expand_try(sp->_key_ok,
-				       ch, &count, (size_t) 0)) != 0
-		   && _nc_remove_key(&(sp->_key_ok), ch)) {
-		code = _nc_add_to_try(&(sp->_keytry), s, ch);
-		free(s);
-		count = 0;
-		if (code != OK)
-		    break;
+				       ch, &count, (size_t) 0)) != 0) {
+		if (_nc_remove_key(&(sp->_key_ok), ch)) {
+		    code = _nc_add_to_try(&(sp->_keytry), s, ch);
+		    free(s);
+		    count = 0;
+		    if (code != OK)
+			break;
+		} else {
+		    free(s);
+		}
 	    }
 	} else {
 	    while ((s = _nc_expand_try(sp->_keytry,
-				       ch, &count, (size_t) 0)) != 0
-		   && _nc_remove_key(&(sp->_keytry), ch)) {
-		code = _nc_add_to_try(&(sp->_key_ok), s, ch);
-		free(s);
-		count = 0;
-		if (code != OK)
-		    break;
+				       ch, &count, (size_t) 0)) != 0) {
+		if (_nc_remove_key(&(sp->_keytry), ch)) {
+		    code = _nc_add_to_try(&(sp->_key_ok), s, ch);
+		    free(s);
+		    count = 0;
+		    if (code != OK)
+			break;
+		} else {
+		    free(s);
+		}
 	    }
 	}
     }
@@ -1382,3 +1489,66 @@
 	drv_kyExist,		/* kyExist */
 	drv_cursorSet		/* cursorSet */
 };
+
+#ifdef EXP_WIN32_DRIVER
+/*
+ * The terminfo driver is mandatory and must always be present.
+ * So this is the natural place for the driver initialisation
+ * logic.
+ */
+
+typedef struct DriverEntry {
+    const char *name;
+    TERM_DRIVER *driver;
+} DRIVER_ENTRY;
+
+static DRIVER_ENTRY DriverTable[] =
+{
+#ifdef _NC_WINDOWS
+    {"win32console", &_nc_WIN_DRIVER},
+#endif
+    {"tinfo", &_nc_TINFO_DRIVER}	/* must be last */
+};
+
+NCURSES_EXPORT(int)
+_nc_get_driver(TERMINAL_CONTROL_BLOCK * TCB, const char *name, int *errret)
+{
+    int code = ERR;
+    size_t i;
+    TERM_DRIVER *res = (TERM_DRIVER *) 0;
+    TERM_DRIVER *use = 0;
+
+    T((T_CALLED("_nc_get_driver(%p, %s, %p)"),
+       (void *) TCB, NonNull(name), (void *) errret));
+
+    assert(TCB != 0);
+
+    for (i = 0; i < SIZEOF(DriverTable); i++) {
+	res = DriverTable[i].driver;
+#ifdef _NC_WINDOWS
+	if ((i + 1) == SIZEOF(DriverTable)) {
+	    /* For Windows >= 10.0.17763 Windows Console interface implements
+	       virtual Terminal functionality.
+	       If on Windows td_CanHandle returned FALSE although the terminal
+	       name is empty, we default to ms-terminal as tinfo TERM type.
+	     */
+	    if (name == 0 || *name == 0 || (strcmp(name, "unknown") == 0)) {
+		name = MS_TERMINAL;
+		T(("Set TERM=%s", name));
+	    }
+	}
+#endif
+	if (strcmp(DriverTable[i].name, res->td_name(TCB)) == 0) {
+	    if (res->td_CanHandle(TCB, name, errret)) {
+		use = res;
+		break;
+	    }
+	}
+    }
+    if (use != 0) {
+	TCB->drv = use;
+	code = OK;
+    }
+    returnCode(code);
+}
+#endif /* EXP_WIN32_DRIVER */
diff --git a/ncurses/tinfo/trim_sgr0.c b/ncurses/tinfo/trim_sgr0.c
index ec5e2b7..177dcd8 100644
--- a/ncurses/tinfo/trim_sgr0.c
+++ b/ncurses/tinfo/trim_sgr0.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2005-2010,2012 Free Software Foundation, Inc.              *
+ * Copyright 2020-2021,2023 Thomas E. Dickey                                *
+ * Copyright 2005-2012,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            *
@@ -36,22 +37,18 @@
 
 #include <tic.h>
 
-MODULE_ID("$Id: trim_sgr0.c,v 1.15 2012/12/15 20:57:17 tom Exp $")
+MODULE_ID("$Id: trim_sgr0.c,v 1.22 2023/09/23 18:47:56 tom Exp $")
 
 #undef CUR
 #define CUR tp->
 
-#define CSI       233
-#define ESC       033		/* ^[ */
-#define L_BRACK   '['
-
 static char *
-set_attribute_9(TERMTYPE *tp, int flag)
+set_attribute_9(TERMTYPE2 *tp, int flag)
 {
     const char *value;
     char *result;
 
-    value = tparm(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, flag);
+    value = TIPARM_9(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, flag);
     if (PRESENT(value))
 	result = strdup(value);
     else
@@ -64,9 +61,9 @@
 {
     int result = 0;
     if (s != 0) {
-	if (UChar(s[0]) == CSI)
+	if (UChar(s[0]) == CSI_CHR)
 	    result = 1;
-	else if (s[0] == ESC && s[1] == L_BRACK)
+	else if (s[0] == ESC_CHR && s[1] == L_BLOCK)
 	    result = 2;
     }
     return result;
@@ -221,7 +218,7 @@
 }
 
 /*
- * While 'sgr0' is the "same" as termcap 'me', there is a compatibility issue. 
+ * While 'sgr0' is the "same" as termcap 'me', there is a compatibility issue.
  * The sgr/sgr0 capabilities include setting/clearing alternate character set
  * mode.  A termcap application cannot use sgr, so sgr0 strings that reset
  * alternate character set mode will be misinterpreted.  Here, we remove those
@@ -232,7 +229,7 @@
  * an error occurs, or the original sgr0 if no change is needed.
  */
 NCURSES_EXPORT(char *)
-_nc_trim_sgr0(TERMTYPE *tp)
+_nc_trim_sgr0(TERMTYPE2 *tp)
 {
     char *result = exit_attribute_mode;
 
@@ -263,7 +260,7 @@
 	    /*
 	     * If rmacs is a substring of sgr(0), remove that chunk.
 	     */
-	    if (exit_alt_charset_mode != 0) {
+	    if (PRESENT(exit_alt_charset_mode)) {
 		TR(TRACE_DATABASE, ("scan for rmacs %s", _nc_visbuf(exit_alt_charset_mode)));
 		j = strlen(off);
 		k = strlen(exit_alt_charset_mode);
diff --git a/ncurses/tinfo/use_screen.c b/ncurses/tinfo/use_screen.c
index 6a0297c..a4bf932 100644
--- a/ncurses/tinfo/use_screen.c
+++ b/ncurses/tinfo/use_screen.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 2007-2008,2009 Free Software Foundation, Inc.              *
+ * Copyright 2018,2020 Thomas E. Dickey                                     *
+ * Copyright 2007-2009,2016 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            *
@@ -32,15 +33,19 @@
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: use_screen.c,v 1.8 2009/10/24 22:40:20 tom Exp $")
+MODULE_ID("$Id: use_screen.c,v 1.12 2020/02/02 23:34:34 tom Exp $")
 
 NCURSES_EXPORT(int)
 use_screen(SCREEN *screen, NCURSES_SCREEN_CB func, void *data)
 {
     SCREEN *save_SP;
     int code = OK;
+    TR_FUNC_BFR(1);
 
-    T((T_CALLED("use_screen(%p,%p,%p)"), (void *) screen, func, (void *) data));
+    T((T_CALLED("use_screen(%p,%s,%p)"),
+       (void *) screen,
+       TR_FUNC_ARG(0, func),
+       (void *) data));
 
     /*
      * FIXME - add a flag so a given thread can check if _it_ has already
diff --git a/ncurses/tinfo/write_entry.c b/ncurses/tinfo/write_entry.c
index b2edd5d..8ccca9e 100644
--- a/ncurses/tinfo/write_entry.c
+++ b/ncurses/tinfo/write_entry.c
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2013,2014 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            *
@@ -41,40 +42,74 @@
 
 #include <tic.h>
 
+MODULE_ID("$Id: write_entry.c,v 1.132 2024/04/20 17:58:51 tom Exp $")
+
 #if 1
 #define TRACE_OUT(p) DEBUG(2, p)
+#define TRACE_NUM(n) if (VALID_NUMERIC(Numbers[n])) { \
+	TRACE_OUT(("put Numbers[%u]=%d", (unsigned) (n), Numbers[n])); }
 #else
 #define TRACE_OUT(p)		/*nothing */
+#define TRACE_NUM(n)		/* nothing */
 #endif
 
-MODULE_ID("$Id: write_entry.c,v 1.92 2014/11/01 14:47:00 tom Exp $")
+/*
+ * FIXME: special case to work around Cygwin bug in link(), which updates
+ * the target file's timestamp.
+ */
+#if HAVE_LINK && !USE_SYMLINKS && !MIXEDCASE_FILENAMES && defined(__CYGWIN__)
+#define LINK_TOUCHES 1
+#else
+#define LINK_TOUCHES 0
+#endif
 
 static int total_written;
+static int total_parts;
+static int total_size;
 
 static int make_db_root(const char *);
-static int write_object(TERMTYPE *, char *, unsigned *, unsigned);
 
 #if !USE_HASHED_DB
 static void
-write_file(char *filename, TERMTYPE *tp)
+write_file(char *filename, TERMTYPE2 *tp)
 {
     char buffer[MAX_ENTRY_SIZE];
     unsigned limit = sizeof(buffer);
     unsigned offset = 0;
 
-    FILE *fp = (_nc_access(filename, W_OK) == 0) ? fopen(filename, "wb") : 0;
-    if (fp == 0) {
-	perror(filename);
-	_nc_syserr_abort("can't open %s/%s", _nc_tic_dir(0), filename);
-    }
-    DEBUG(1, ("Created %s", filename));
+    if (_nc_write_object(tp, buffer, &offset, limit) == ERR) {
+	_nc_warning("entry is larger than %u bytes", limit);
+    } else {
+	FILE *fp = ((_nc_access(filename, W_OK) == 0)
+		    ? safe_fopen(filename, BIN_W)
+		    : 0);
+	size_t actual;
 
-    if (write_object(tp, buffer, &offset, limit) == ERR
-	|| fwrite(buffer, sizeof(char), (size_t) offset, fp) != offset) {
-	_nc_syserr_abort("error writing %s/%s", _nc_tic_dir(0), filename);
-    }
+	if (fp == 0) {
+	    perror(filename);
+	    _nc_syserr_abort("cannot open %s/%s", _nc_tic_dir(0), filename);
+	}
 
-    fclose(fp);
+	actual = fwrite(buffer, sizeof(char), (size_t) offset, fp);
+	if (actual != offset) {
+	    int myerr = ferror(fp) ? errno : 0;
+	    if (myerr) {
+		_nc_syserr_abort("error writing %s/%s: %s",
+				 _nc_tic_dir(NULL),
+				 filename,
+				 strerror(myerr));
+	    } else {
+		_nc_syserr_abort("error writing %s/%s: %u bytes vs actual %lu",
+				 _nc_tic_dir(NULL),
+				 filename,
+				 offset,
+				 (unsigned long) actual);
+	    }
+	} else {
+	    fclose(fp);
+	    DEBUG(1, ("Created %s", filename));
+	}
+    }
 }
 
 /*
@@ -93,18 +128,16 @@
     char dir[sizeof(LEAF_FMT)];
     char *s = 0;
 
-    if (code == 0 || (s = (strchr) (dirnames, code)) == 0)
+    if (code == 0 || (s = (strchr) (dirnames, code)) == 0) {
 	_nc_err_abort("Illegal terminfo subdirectory \"" LEAF_FMT "\"", code);
-
-    if (verified[s - dirnames])
-	return;
-
-    _nc_SPRINTF(dir, _nc_SLIMIT(sizeof(dir)) LEAF_FMT, code);
-    if (make_db_root(dir) < 0) {
-	_nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir);
+    } else if (!verified[s - dirnames]) {
+	_nc_SPRINTF(dir, _nc_SLIMIT(sizeof(dir)) LEAF_FMT, code);
+	if (make_db_root(dir) < 0) {
+	    _nc_err_abort("%s/%s: permission denied", _nc_tic_dir(NULL), dir);
+	} else {
+	    verified[s - dirnames] = TRUE;
+	}
     }
-
-    verified[s - dirnames] = TRUE;
 }
 #endif /* !USE_HASHED_DB */
 
@@ -112,7 +145,7 @@
 make_db_path(char *dst, const char *src, size_t limit)
 {
     int rc = -1;
-    const char *top = _nc_tic_dir(0);
+    const char *top = _nc_tic_dir(NULL);
 
     if (src == top || _nc_is_abs_path(src)) {
 	if (strlen(src) + 1 <= limit) {
@@ -120,7 +153,7 @@
 	    rc = 0;
 	}
     } else {
-	if (strlen(top) + strlen(src) + 2 <= limit) {
+	if ((strlen(top) + strlen(src) + 6) <= limit) {
 	    _nc_SPRINTF(dst, _nc_SLIMIT(limit) "%s/%s", top, src);
 	    rc = 0;
 	}
@@ -165,9 +198,9 @@
 #else
 	struct stat statbuf;
 
-	if ((rc = stat(path, &statbuf)) < 0) {
+	if ((rc = stat(path, &statbuf)) == -1) {
 	    rc = mkdir(path
-#if !defined(__MINGW32__)
+#ifndef _NC_WINDOWS
 		       ,0777
 #endif
 		);
@@ -189,26 +222,30 @@
 {
     const char *destination;
     char actual[PATH_MAX];
+    bool specific = (dir != NULL);
 
-    if (dir == 0
-#ifndef USE_ROOT_ENVIRON
-	&& use_terminfo_vars()
-#endif
-	)
+    if (!specific && use_terminfo_vars())
 	dir = getenv("TERMINFO");
 
-    if (dir != 0)
+    if (dir != NULL)
 	(void) _nc_tic_dir(dir);
 
-    destination = _nc_tic_dir(0);
+    destination = _nc_tic_dir(NULL);
     if (make_db_root(destination) < 0) {
-	char *home = _nc_home_terminfo();
+	bool success = FALSE;
 
-	if (home != 0) {
-	    destination = home;
-	    if (make_db_root(destination) < 0)
-		_nc_err_abort("%s: permission denied (errno %d)",
-			      destination, errno);
+	if (!specific) {
+	    char *home = _nc_home_terminfo();
+
+	    if (home != NULL) {
+		destination = home;
+		if (make_db_root(destination) == 0)
+		    success = TRUE;
+	    }
+	}
+	if (!success) {
+	    _nc_err_abort("%s: permission denied (errno %d)",
+			  destination, errno);
 	}
     }
 
@@ -220,10 +257,10 @@
     make_db_path(actual, destination, sizeof(actual));
 #else
     if (chdir(_nc_tic_dir(destination)) < 0
-	|| getcwd(actual, sizeof(actual)) == 0)
+	|| getcwd(actual, sizeof(actual)) == NULL)
 	_nc_err_abort("%s: not a directory", destination);
 #endif
-    _nc_keep_tic_dir(strdup(actual));
+    _nc_keep_tic_dir(actual);
 }
 
 /*
@@ -247,7 +284,7 @@
  */
 
 NCURSES_EXPORT(void)
-_nc_write_entry(TERMTYPE *const tp)
+_nc_write_entry(TERMTYPE2 *const tp)
 {
 #if USE_HASHED_DB
 
@@ -268,6 +305,9 @@
 #endif
 #endif /* USE_SYMLINKS */
 
+    unsigned limit2 = sizeof(filename) - (2 + LEAF_LEN);
+    char saved = '\0';
+
     static int call_count;
     static time_t start_time;	/* time at start of writes */
 
@@ -299,8 +339,9 @@
     if (ptr != name_list) {
 	*ptr = '\0';
 
-	for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++)
-	    continue;
+	for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++) {
+	    /* EMPTY */ ;
+	}
 
 	if (*ptr == '\0')
 	    other_names = ptr;
@@ -316,11 +357,11 @@
     _nc_set_type(first_name);
 
 #if USE_HASHED_DB
-    if (write_object(tp, buffer + 1, &offset, limit - 1) != ERR) {
-	DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE);
+    if (_nc_write_object(tp, buffer + 1, &offset, limit - 1) != ERR) {
+	DB *capdb = _nc_db_open(_nc_tic_dir(NULL), TRUE);
 	DBT key, data;
 
-	if (capdb != 0) {
+	if (capdb != NULL) {
 	    buffer[0] = 0;
 
 	    memset(&key, 0, sizeof(key));
@@ -343,6 +384,8 @@
 		       sizeof(buffer) - 1);
 	    data.size = name_size + 1;
 
+	    total_size += (int) data.size;
+	    total_parts++;
 	    _nc_db_put(capdb, &key, &data);
 
 	    while (*other_names != '\0') {
@@ -357,6 +400,8 @@
 		key.data = ptr;
 		key.size = strlen(ptr);
 
+		total_size += (int) data.size;
+		total_parts++;
 		_nc_db_put(capdb, &key, &data);
 	    }
 	}
@@ -366,11 +411,19 @@
 	start_time = 0;
     }
 
-    if (strlen(first_name) >= sizeof(filename) - (2 + LEAF_LEN))
+    if (strlen(first_name) >= limit2) {
 	_nc_warning("terminal name too long.");
+	saved = first_name[limit2];
+	first_name[limit2] = '\0';
+    }
 
     _nc_SPRINTF(filename, _nc_SLIMIT(sizeof(filename))
-		LEAF_FMT "/%s", first_name[0], first_name);
+		LEAF_FMT "/%.*s", UChar(first_name[0]),
+		(int) (sizeof(filename) - (LEAF_LEN + 2)),
+		first_name);
+
+    if (saved)
+	first_name[limit2] = saved;
 
     /*
      * Has this primary name been written since the first call to
@@ -402,10 +455,10 @@
     write_file(filename, tp);
 
     if (start_time == 0) {
-	if (stat(filename, &statbuf) < 0
+	if (stat(filename, &statbuf) == -1
 	    || (start_time = statbuf.st_mtime) == 0) {
 	    _nc_syserr_abort("error obtaining time from %s/%s",
-			     _nc_tic_dir(0), filename);
+			     _nc_tic_dir(NULL), filename);
 	}
     }
     while (*other_names != '\0') {
@@ -420,32 +473,38 @@
 	    _nc_warning("terminal alias %s too long.", ptr);
 	    continue;
 	}
-	if (strchr(ptr, '/') != 0) {
+	if (strchr(ptr, '/') != NULL) {
 	    _nc_warning("cannot link alias %s.", ptr);
 	    continue;
 	}
 
 	check_writeable(ptr[0]);
 	_nc_SPRINTF(linkname, _nc_SLIMIT(sizeof(linkname))
-		    LEAF_FMT "/%s", ptr[0], ptr);
+		    LEAF_FMT "/%.*s", ptr[0],
+		    (int) sizeof(linkname) - (2 + LEAF_LEN), ptr);
 
 	if (strcmp(filename, linkname) == 0) {
 	    _nc_warning("self-synonym ignored");
-	} else if (stat(linkname, &statbuf) >= 0 &&
-		   statbuf.st_mtime < start_time) {
+	}
+#if !LINK_TOUCHES
+	else if (stat(linkname, &statbuf) >= 0 &&
+		 statbuf.st_mtime < start_time) {
 	    _nc_warning("alias %s multiply defined.", ptr);
-	} else if (_nc_access(linkname, W_OK) == 0)
+	}
+#endif
+	else if (_nc_access(linkname, W_OK) == 0)
 #if HAVE_LINK
 	{
 	    int code;
 #if USE_SYMLINKS
-	    if (first_name[0] == linkname[0])
-		strncpy(symlinkname, first_name, sizeof(symlinkname) - 1);
-	    else {
-		_nc_STRCPY(symlinkname, "../", sizeof(suymlinkname));
-		strncat(symlinkname, filename, sizeof(symlinkname) - 4);
+#define MY_SIZE sizeof(symlinkname) - 1
+	    if (first_name[0] == linkname[0]) {
+		_nc_STRNCPY(symlinkname, first_name, MY_SIZE);
+	    } else {
+		_nc_STRCPY(symlinkname, "../", sizeof(symlinkname));
+		_nc_STRNCPY(symlinkname + 3, filename, MY_SIZE - 3);
 	    }
-	    symlinkname[sizeof(symlinkname) - 1] = '\0';
+	    symlinkname[MY_SIZE] = '\0';
 #endif /* USE_SYMLINKS */
 #if HAVE_REMOVE
 	    code = remove(linkname);
@@ -472,9 +531,9 @@
 		    write_file(linkname, tp);
 		else {
 #if MIXEDCASE_FILENAMES
-		    _nc_syserr_abort("can't link %s to %s", filename, linkname);
+		    _nc_syserr_abort("cannot link %s to %s", filename, linkname);
 #else
-		    _nc_warning("can't link %s to %s (errno=%d)", filename,
+		    _nc_warning("cannot link %s to %s (errno=%d)", filename,
 				linkname, errno);
 #endif
 		}
@@ -542,7 +601,7 @@
     return nextfree;
 }
 
-static void
+static size_t
 convert_shorts(unsigned char *buf, short *Numbers, size_t count)
 {
     size_t i;
@@ -557,14 +616,49 @@
 	    TRACE_OUT(("put Numbers[%u]=%d", (unsigned) i, Numbers[i]));
 	}
     }
+    return SIZEOF_SHORT;
 }
 
+#if NCURSES_EXT_NUMBERS
+static size_t
+convert_16bit(unsigned char *buf, NCURSES_INT2 *Numbers, size_t count)
+{
+    size_t i, j;
+    size_t size = SIZEOF_SHORT;
+    for (i = 0; i < count; i++) {
+	unsigned value = (unsigned) Numbers[i];
+	TRACE_NUM(i);
+	for (j = 0; j < size; ++j) {
+	    *buf++ = value & 0xff;
+	    value >>= 8;
+	}
+    }
+    return size;
+}
+
+static size_t
+convert_32bit(unsigned char *buf, NCURSES_INT2 *Numbers, size_t count)
+{
+    size_t i, j;
+    size_t size = SIZEOF_INT2;
+    for (i = 0; i < count; i++) {
+	unsigned value = (unsigned) Numbers[i];
+	TRACE_NUM(i);
+	for (j = 0; j < size; ++j) {
+	    *buf++ = value & 0xff;
+	    value >>= 8;
+	}
+    }
+    return size;
+}
+#endif
+
 #define even_boundary(value) \
 	    ((value) % 2 != 0 && Write(&zero, sizeof(char), 1) != 1)
 
 #if NCURSES_XNAMES
 static unsigned
-extended_Booleans(TERMTYPE *tp)
+extended_Booleans(TERMTYPE2 *tp)
 {
     unsigned result = 0;
     unsigned i;
@@ -577,7 +671,7 @@
 }
 
 static unsigned
-extended_Numbers(TERMTYPE *tp)
+extended_Numbers(TERMTYPE2 *tp)
 {
     unsigned result = 0;
     unsigned i;
@@ -590,7 +684,7 @@
 }
 
 static unsigned
-extended_Strings(TERMTYPE *tp)
+extended_Strings(TERMTYPE2 *tp)
 {
     unsigned short result = 0;
     unsigned short i;
@@ -607,7 +701,7 @@
  * clause - discard the unneeded data.
  */
 static bool
-extended_object(TERMTYPE *tp)
+extended_object(TERMTYPE2 *tp)
 {
     bool result = FALSE;
 
@@ -620,11 +714,11 @@
 }
 #endif
 
-static int
-write_object(TERMTYPE *tp, char *buffer, unsigned *offset, unsigned limit)
+NCURSES_EXPORT(int)
+_nc_write_object(TERMTYPE2 *tp, char *buffer, unsigned *offset, unsigned limit)
 {
     char *namelist;
-    size_t namelen, boolmax, nummax, strmax;
+    size_t namelen, boolmax, nummax, strmax, numlen;
     char zero = '\0';
     size_t i;
     int nextfree;
@@ -633,6 +727,12 @@
     unsigned last_bool = BOOLWRITE;
     unsigned last_num = NUMWRITE;
     unsigned last_str = STRWRITE;
+#if NCURSES_EXT_NUMBERS
+    bool need_ints = FALSE;
+    size_t (*convert_numbers) (unsigned char *, NCURSES_INT2 *, size_t);
+#else
+#define convert_numbers convert_shorts
+#endif
 
 #if NCURSES_XNAMES
     /*
@@ -653,14 +753,21 @@
 
     boolmax = 0;
     for (i = 0; i < last_bool; i++) {
-	if (tp->Booleans[i] == TRUE)
+	if (tp->Booleans[i] == TRUE) {
 	    boolmax = i + 1;
+	}
     }
 
     nummax = 0;
     for (i = 0; i < last_num; i++) {
-	if (tp->Numbers[i] != ABSENT_NUMERIC)
+	if (tp->Numbers[i] != ABSENT_NUMERIC) {
 	    nummax = i + 1;
+#if NCURSES_EXT_NUMBERS
+	    if (tp->Numbers[i] > MAX_OF_TYPE(NCURSES_COLOR_T)) {
+		need_ints = TRUE;
+	    }
+#endif
+	}
     }
 
     strmax = 0;
@@ -672,8 +779,19 @@
     nextfree = compute_offsets(tp->Strings, strmax, offsets);
 
     /* fill in the header */
+#if NCURSES_EXT_NUMBERS
+    if (need_ints) {
+	convert_numbers = convert_32bit;
+	LITTLE_ENDIAN(buf, MAGIC2);
+    } else {
+	convert_numbers = convert_16bit;
+	LITTLE_ENDIAN(buf, MAGIC);
+    }
+#else
     LITTLE_ENDIAN(buf, MAGIC);
-    LITTLE_ENDIAN(buf + 2, min(namelen, MAX_NAME_SIZE + 1));
+#endif
+    namelen = Min(namelen, MAX_NAME_SIZE + 1);
+    LITTLE_ENDIAN(buf + 2, namelen);
     LITTLE_ENDIAN(buf + 4, boolmax);
     LITTLE_ENDIAN(buf + 6, nummax);
     LITTLE_ENDIAN(buf + 8, strmax);
@@ -682,62 +800,81 @@
     /* write out the header */
     TRACE_OUT(("Header of %s @%d", namelist, *offset));
     if (Write(buf, 12, 1) != 1
-	|| Write(namelist, sizeof(char), namelen) != namelen)
-	  return (ERR);
-
-    for (i = 0; i < boolmax; i++)
-	if (tp->Booleans[i] == TRUE)
-	    buf[i] = TRUE;
-	else
-	    buf[i] = FALSE;
-    if (Write(buf, sizeof(char), boolmax) != boolmax)
-	  return (ERR);
-
-    if (even_boundary(namelen + boolmax))
+	|| Write(namelist, sizeof(char), namelen) != namelen) {
 	return (ERR);
+    }
+
+    for (i = 0; i < boolmax; i++) {
+	if (tp->Booleans[i] == TRUE) {
+	    buf[i] = TRUE;
+	} else {
+	    buf[i] = FALSE;
+	}
+    }
+    if (Write(buf, sizeof(char), boolmax) != boolmax) {
+	return (ERR);
+    }
+
+    if (even_boundary(namelen + boolmax)) {
+	return (ERR);
+    }
 
     TRACE_OUT(("Numerics begin at %04x", *offset));
 
     /* the numerics */
-    convert_shorts(buf, tp->Numbers, nummax);
-    if (Write(buf, 2, nummax) != nummax)
+    numlen = convert_numbers(buf, tp->Numbers, nummax);
+    if (Write(buf, numlen, nummax) != nummax) {
 	return (ERR);
+    }
 
     TRACE_OUT(("String offsets begin at %04x", *offset));
 
     /* the string offsets */
     convert_shorts(buf, offsets, strmax);
-    if (Write(buf, 2, strmax) != strmax)
+    if (Write(buf, SIZEOF_SHORT, strmax) != strmax) {
 	return (ERR);
+    }
 
     TRACE_OUT(("String table begins at %04x", *offset));
 
     /* the strings */
-    for (i = 0; i < strmax; i++)
-	if (VALID_STRING(tp->Strings[i]))
-	    if (!WRITE_STRING(tp->Strings[i]))
+    for (i = 0; i < strmax; i++) {
+	if (VALID_STRING(tp->Strings[i])) {
+	    if (!WRITE_STRING(tp->Strings[i])) {
 		return (ERR);
+	    }
+	}
+    }
 
 #if NCURSES_XNAMES
     if (extended_object(tp)) {
-	unsigned extcnt = (unsigned) NUM_EXT_NAMES(tp);
+	unsigned ext_total = (unsigned) NUM_EXT_NAMES(tp);
+	unsigned ext_usage = ext_total;
 
-	if (even_boundary(nextfree))
+	if (even_boundary(nextfree)) {
 	    return (ERR);
+	}
 
 	nextfree = compute_offsets(tp->Strings + STRCOUNT,
 				   (size_t) tp->ext_Strings,
 				   offsets);
 	TRACE_OUT(("after extended string capabilities, nextfree=%d", nextfree));
 
-	if (tp->ext_Strings >= SIZEOF(offsets))
+	if (tp->ext_Strings >= SIZEOF(offsets)) {
 	    return (ERR);
+	}
 
 	nextfree += compute_offsets(tp->ext_Names,
-				    (size_t) extcnt,
+				    (size_t) ext_total,
 				    offsets + tp->ext_Strings);
 	TRACE_OUT(("after extended capnames, nextfree=%d", nextfree));
-	strmax = tp->ext_Strings + extcnt;
+	strmax = tp->ext_Strings + ext_total;
+	for (i = 0; i < tp->ext_Strings; ++i) {
+	    if (VALID_STRING(tp->Strings[i + STRCOUNT])) {
+		ext_usage++;
+	    }
+	}
+	TRACE_OUT(("will write %u/%lu strings", ext_usage, (unsigned long) strmax));
 
 	/*
 	 * Write the extended header
@@ -745,26 +882,30 @@
 	LITTLE_ENDIAN(buf + 0, tp->ext_Booleans);
 	LITTLE_ENDIAN(buf + 2, tp->ext_Numbers);
 	LITTLE_ENDIAN(buf + 4, tp->ext_Strings);
-	LITTLE_ENDIAN(buf + 6, strmax);
+	LITTLE_ENDIAN(buf + 6, ext_usage);
 	LITTLE_ENDIAN(buf + 8, nextfree);
 	TRACE_OUT(("WRITE extended-header @%d", *offset));
-	if (Write(buf, 10, 1) != 1)
+	if (Write(buf, 10, 1) != 1) {
 	    return (ERR);
+	}
 
 	TRACE_OUT(("WRITE %d booleans @%d", tp->ext_Booleans, *offset));
 	if (tp->ext_Booleans
 	    && Write(tp->Booleans + BOOLCOUNT, sizeof(char),
-		     tp->ext_Booleans) != tp->ext_Booleans)
-	      return (ERR);
-
-	if (even_boundary(tp->ext_Booleans))
+		     tp->ext_Booleans) != tp->ext_Booleans) {
 	    return (ERR);
+	}
+
+	if (even_boundary(tp->ext_Booleans)) {
+	    return (ERR);
+	}
 
 	TRACE_OUT(("WRITE %d numbers @%d", tp->ext_Numbers, *offset));
 	if (tp->ext_Numbers) {
-	    convert_shorts(buf, tp->Numbers + NUMCOUNT, (size_t) tp->ext_Numbers);
-	    if (Write(buf, 2, tp->ext_Numbers) != tp->ext_Numbers)
+	    numlen = convert_numbers(buf, tp->Numbers + NUMCOUNT, (size_t) tp->ext_Numbers);
+	    if (Write(buf, numlen, tp->ext_Numbers) != tp->ext_Numbers) {
 		return (ERR);
+	    }
 	}
 
 	/*
@@ -773,8 +914,9 @@
 	 */
 	convert_shorts(buf, offsets, strmax);
 	TRACE_OUT(("WRITE offsets @%d", *offset));
-	if (Write(buf, 2, strmax) != strmax)
+	if (Write(buf, SIZEOF_SHORT, strmax) != strmax) {
 	    return (ERR);
+	}
 
 	/*
 	 * Write the string table after the offset tables so we do not
@@ -784,24 +926,28 @@
 	    if (VALID_STRING(tp->Strings[i + STRCOUNT])) {
 		TRACE_OUT(("WRITE ext_Strings[%d]=%s", (int) i,
 			   _nc_visbuf(tp->Strings[i + STRCOUNT])));
-		if (!WRITE_STRING(tp->Strings[i + STRCOUNT]))
+		if (!WRITE_STRING(tp->Strings[i + STRCOUNT])) {
 		    return (ERR);
+		}
 	    }
 	}
 
 	/*
 	 * Write the extended names
 	 */
-	for (i = 0; i < extcnt; i++) {
+	for (i = 0; i < ext_total; i++) {
 	    TRACE_OUT(("WRITE ext_Names[%d]=%s", (int) i, tp->ext_Names[i]));
-	    if (!WRITE_STRING(tp->ext_Names[i]))
+	    if (!WRITE_STRING(tp->ext_Names[i])) {
 		return (ERR);
+	    }
 	}
 
     }
 #endif /* NCURSES_XNAMES */
 
     total_written++;
+    total_parts++;
+    total_size = total_size + (int) (*offset + 1);
     return (OK);
 }
 
@@ -811,5 +957,7 @@
 NCURSES_EXPORT(int)
 _nc_tic_written(void)
 {
+    TR(TRACE_DATABASE, ("_nc_tic_written %d entries, %d parts, %d size",
+			total_written, total_parts, total_size));
     return total_written;
 }