File ncurses.patch of Package tmux.32184

diff --git a/compat.h b/compat.h
index cabdf3ad..7b543128 100644
--- a/compat.h
+++ b/compat.h
@@ -435,6 +435,11 @@ int		 utf8proc_mbtowc(wchar_t *, const char *, size_t);
 int		 utf8proc_wctomb(char *, wchar_t);
 #endif
 
+/* tparm.c */
+#ifndef HAVE_TIPARM
+char		*compat_tiparm(const char *, ...);
+#endif
+
 #ifdef NEED_FUZZING
 /* tmux.c */
 #define main __weak main
diff --git a/compat/tiparm.c b/compat/tiparm.c
new file mode 100644
index 00000000..01d0adf0
--- /dev/null
+++ b/compat/tiparm.c
@@ -0,0 +1,655 @@
+/* $NetBSD: tparm.c,v 1.19 2021/08/27 18:40:28 rillig Exp $ */
+
+/*
+ * Copyright (c) 2009, 2011, 2013 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if 0
+__RCSID("$NetBSD: tparm.c,v 1.19 2021/08/27 18:40:28 rillig Exp $");
+#endif
+#include <sys/param.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if 0
+#include <term_private.h>
+#include <term.h>
+#endif
+
+#define TPARM_MAX 9	/* not likely to change */
+
+typedef struct {
+	/* Output buffer for tparm */
+	char *_buf;
+	size_t _buflen;
+	size_t _bufpos;
+	/* A-Z static variables for tparm  */
+	long _snums[26];
+} TERMINAL;
+
+#define _DIAGASSERT(x)
+
+#define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3)
+#define BUFINC 128	/* Size to increment the terminal buffer by */
+
+#define VA_LONG_LONG	1
+#define VA_CHAR_INT	2
+//#define VA_CHAR_LONG	3	/* No need for this yet */
+
+static TERMINAL *dumbterm; /* For non thread safe functions */
+
+typedef struct {
+	long nums[20];
+	char *strings[20];
+	size_t offset;
+} TPSTACK;
+
+typedef struct {
+	long num;
+	char *string;
+} TPVAR;
+
+static int
+push(long num, char *string, TPSTACK *stack)
+{
+	if (stack->offset >= sizeof(stack->nums)) {
+		errno = E2BIG;
+		return -1;
+	}
+	stack->nums[stack->offset] = num;
+	stack->strings[stack->offset] = string;
+	stack->offset++;
+	return 0;
+}
+
+static int
+pop(long *num, char **string, TPSTACK *stack)
+{
+	if (stack->offset == 0) {
+		if (num)
+			*num = 0;
+		if (string)
+			*string = NULL;
+		errno = E2BIG;
+		return -1;
+	}
+	stack->offset--;
+	if (num)
+		*num = stack->nums[stack->offset];
+	if (string)
+		*string = stack->strings[stack->offset];
+	return 0;
+}
+
+static char *
+checkbuf(TERMINAL *term, size_t len)
+{
+	char *buf;
+
+	if (term->_bufpos + len >= term->_buflen) {
+		len = term->_buflen + MAX(len, BUFINC);
+		buf = realloc(term->_buf, len);
+		if (buf == NULL)
+			return NULL;
+		term->_buf = buf;
+		term->_buflen = len;
+	}
+	return term->_buf;
+}
+
+static size_t
+ochar(TERMINAL *term, int c)
+{
+	if (c == 0)
+		c = 0200;
+	/* Check we have space and a terminator */
+	if (checkbuf(term, 2) == NULL)
+		return 0;
+	term->_buf[term->_bufpos++] = (char)c;
+	return 1;
+}
+
+static size_t
+onum(TERMINAL *term, const char *fmt, int num, size_t len)
+{
+	int l;
+	size_t r;
+
+	if (len < LONG_STR_MAX)
+		len = LONG_STR_MAX;
+	if (checkbuf(term, len + 2) == NULL)
+		return 0;
+	l = snprintf(term->_buf + term->_bufpos, len + 2, fmt, num);
+	if (l == -1)
+		return 0;
+	r = (size_t)l;
+	term->_bufpos += r;
+	return r;
+}
+
+/*
+  Make a pass through the string so we can work out
+  which parameters are ints and which are char *.
+  Basically we only use char * if %p[1-9] is followed by %l or %s.
+*/
+static int
+_ti_parm_analyse(const char *str, int *piss, int piss_len)
+{
+	int nparm, lpop;
+	char c;
+
+	nparm = 0;
+	lpop = -1;
+	while ((c = *str++) != '\0') {
+		if (c != '%')
+			continue;
+		c = *str++;
+		switch (c) {
+			case 'l':
+			case 's':
+				if (lpop > 0) {
+					if (lpop <= piss_len)
+						piss[lpop - 1] = 1;
+					else if (piss)
+						errno = E2BIG;
+				}
+				break;
+			case 'p':
+				c = *str++;
+				if (c < '1' || c > '9') {
+					errno = EINVAL;
+					continue;
+				} else {
+					lpop = c - '0';
+					if (lpop > nparm)
+						nparm = lpop;
+				}
+				break;
+			default:
+				lpop = -1;
+		}
+	}
+
+	return nparm;
+}
+
+static char *
+_ti_tiparm(TERMINAL *term, const char *str, int va_type, va_list parms)
+{
+	char c, fmt[64], *fp, *ostr;
+	long val, val2;
+	long dnums[26]; /* dynamic variables a-z, not preserved */
+	size_t l, max, width, precision, olen;
+	TPSTACK stack;
+	TPVAR params[TPARM_MAX];
+	unsigned int done, dot, minus;
+	int piss[TPARM_MAX]; /* Parameter IS String - piss ;) */
+
+	if (str == NULL)
+		return NULL;
+
+	/*
+	  If not passed a terminal, malloc a dummy one.
+	  This means we can preserve buffers and variables per terminal and
+	  still work with non thread safe functions (which sadly are still the
+	  norm and standard).
+	*/
+	if (term == NULL) {
+		if (dumbterm == NULL) {
+			dumbterm = malloc(sizeof(*dumbterm));
+			if (dumbterm == NULL)
+				return NULL;
+			dumbterm->_buflen = 0;
+		}
+		term = dumbterm;
+	}
+
+	term->_bufpos = 0;
+	/* Ensure we have an initial buffer */
+	if (term->_buflen == 0) {
+		term->_buf = malloc(BUFINC);
+		if (term->_buf == NULL)
+			return NULL;
+		term->_buflen = BUFINC;
+	}
+
+	memset(&piss, 0, sizeof(piss));
+	max = (size_t)_ti_parm_analyse(str, piss, TPARM_MAX);
+
+	/* Put our parameters into variables */
+	memset(&params, 0, sizeof(params));
+	for (l = 0; l < max; l++) {
+		if (piss[l]) {
+			if (va_type == VA_LONG_LONG) {
+				/* This only works if char * fits into a long
+				 * on this platform. */
+				if (sizeof(char *) <= sizeof(long))
+					params[l].string =
+					    (char *)va_arg(parms, long);
+				else {
+					errno = ENOTSUP;
+					return NULL;
+				}
+			} else
+				params[l].string = va_arg(parms, char *);
+		} else {
+			if (va_type == VA_CHAR_INT)
+				params[l].num = (long)va_arg(parms, int);
+			else
+				params[l].num = va_arg(parms, long);
+		}
+	}
+
+	memset(&stack, 0, sizeof(stack));
+	while ((c = *str++) != '\0') {
+		if (c != '%' || (c = *str++) == '%') {
+			if (c == '\0')
+				break;
+			if (ochar(term, c) == 0)
+				return NULL;
+			continue;
+		}
+
+		/* Handle formatting. */
+		fp = fmt;
+		*fp++ = '%';
+		done = dot = minus = 0;
+		width = precision = 0;
+		val = 0;
+		while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
+			switch (c) {
+			case 'c':
+			case 's':
+				*fp++ = c;
+				done = 1;
+				break;
+			case 'd':
+			case 'o':
+			case 'x':
+			case 'X':
+				*fp++ = 'l';
+				*fp++ = c;
+				done = 1;
+				break;
+			case '#':
+			case ' ':
+				*fp++ = c;
+				break;
+			case '.':
+				*fp++ = c;
+				if (dot == 0) {
+					dot = 1;
+					width = (size_t)val;
+				} else
+					done = 2;
+				val = 0;
+				break;
+			case ':':
+				minus = 1;
+				break;
+			case '-':
+				if (minus)
+					*fp++ = c;
+				else
+					done = 1;
+				break;
+			default:
+				if (isdigit((unsigned char)c)) {
+					val = (val * 10) + (c - '0');
+					if (val > 10000)
+						done = 2;
+					else
+						*fp++ = c;
+				} else
+					done = 1;
+			}
+			if (done == 0)
+				c = *str++;
+		}
+		if (done == 2) {
+			/* Found an error in the format */
+			fp = fmt + 1;
+			*fp = *str;
+			olen = 0;
+		} else {
+			if (dot == 0)
+				width = (size_t)val;
+			else
+				precision = (size_t)val;
+			olen = MAX(width, precision);
+		}
+		*fp++ = '\0';
+
+		/* Handle commands */
+		switch (c) {
+		case 'c':
+			pop(&val, NULL, &stack);
+			if (ochar(term, (unsigned char)val) == 0)
+				return NULL;
+			break;
+		case 's':
+			pop(NULL, &ostr, &stack);
+			if (ostr != NULL) {
+				int r;
+
+				l = strlen(ostr);
+				if (l < (size_t)olen)
+					l = olen;
+				if (checkbuf(term, (size_t)(l + 1)) == NULL)
+					return NULL;
+				r = snprintf(term->_buf + term->_bufpos, l + 1,
+				    fmt, ostr);
+				if (r != -1)
+					term->_bufpos += (size_t)r;
+			}
+			break;
+		case 'l':
+			pop(NULL, &ostr, &stack);
+			if (ostr == NULL)
+				l = 0;
+			else
+				l = strlen(ostr);
+#ifdef NCURSES_COMPAT_57
+			if (onum(term, "%ld", (long)l, 0) == 0)
+				return NULL;
+#else
+			push((long)l, NULL, &stack);
+#endif
+			break;
+		case 'd':
+		case 'o':
+		case 'x':
+		case 'X':
+			pop(&val, NULL, &stack);
+			if (onum(term, fmt, (int)val, olen) == 0)
+				return NULL;
+			break;
+		case 'p':
+			if (*str < '1' || *str > '9')
+				break;
+			l = (size_t)(*str++ - '1');
+			if (push(params[l].num, params[l].string, &stack))
+				return NULL;
+			break;
+		case 'P':
+			pop(&val, NULL, &stack);
+			if (*str >= 'a' && *str <= 'z')
+				dnums[*str - 'a'] = val;
+			else if (*str >= 'A' && *str <= 'Z')
+				term->_snums[*str - 'A'] = val;
+			break;
+		case 'g':
+			if (*str >= 'a' && *str <= 'z') {
+				if (push(dnums[*str - 'a'], NULL, &stack))
+					return NULL;
+			} else if (*str >= 'A' && *str <= 'Z') {
+				if (push(term->_snums[*str - 'A'],
+					NULL, &stack))
+					return NULL;
+			}
+			break;
+		case 'i':
+			if (piss[0] == 0)
+				params[0].num++;
+			if (piss[1] == 0)
+				params[1].num++;
+			break;
+		case '\'':
+			if (push((long)(unsigned char)*str++, NULL, &stack))
+				return NULL;
+			while (*str != '\0' && *str != '\'')
+				str++;
+			if (*str == '\'')
+				str++;
+			break;
+		case '{':
+			val = 0;
+			for (; isdigit((unsigned char)*str);  str++)
+				val = (val * 10) + (*str - '0');
+			if (push(val, NULL, &stack))
+				return NULL;
+			while (*str != '\0' && *str != '}')
+				str++;
+			if (*str == '}')
+				str++;
+			break;
+		case '+':
+		case '-':
+		case '*':
+		case '/':
+		case 'm':
+		case 'A':
+		case 'O':
+		case '&':
+		case '|':
+		case '^':
+		case '=':
+		case '<':
+		case '>':
+			pop(&val, NULL, &stack);
+			pop(&val2, NULL, &stack);
+			switch (c) {
+			case '+':
+				val = val + val2;
+				break;
+			case '-':
+				val = val2 - val;
+				break;
+			case '*':
+				val = val * val2;
+				break;
+			case '/':
+				val = val ? val2 / val : 0;
+				break;
+			case 'm':
+				val = val ? val2 % val : 0;
+				break;
+			case 'A':
+				val = val && val2;
+				break;
+			case 'O':
+				val = val || val2;
+				break;
+			case '&':
+				val = val & val2;
+				break;
+			case '|':
+				val = val | val2;
+				break;
+			case '^':
+				val = val ^ val2;
+				break;
+			case '=':
+				val = val == val2;
+				break;
+			case '<':
+				val = val2 < val;
+				break;
+			case '>':
+				val = val2 > val;
+				break;
+			}
+			if (push(val, NULL, &stack))
+				return NULL;
+			break;
+		case '!':
+		case '~':
+			pop(&val, NULL, &stack);
+			switch (c) {
+			case '!':
+				val = !val;
+				break;
+			case '~':
+				val = ~val;
+				break;
+			}
+			if (push(val, NULL, &stack))
+				return NULL;
+			break;
+		case '?': /* if */
+			break;
+		case 't': /* then */
+			pop(&val, NULL, &stack);
+			if (val == 0) {
+				l = 0;
+				for (; *str != '\0'; str++) {
+					if (*str != '%')
+						continue;
+					str++;
+					if (*str == '?')
+						l++;
+					else if (*str == ';') {
+						if (l > 0)
+							l--;
+						else {
+							str++;
+							break;
+						}
+					} else if (*str == 'e' && l == 0) {
+						str++;
+						break;
+					}
+				}
+			}
+			break;
+		case 'e': /* else */
+			l = 0;
+			for (; *str != '\0'; str++) {
+				if (*str != '%')
+					continue;
+				str++;
+				if (*str == '?')
+					l++;
+				else if (*str == ';') {
+					if (l > 0)
+						l--;
+					else {
+						str++;
+						break;
+					}
+				}
+			}
+			break;
+		case ';': /* fi */
+			break;
+		}
+	}
+	term->_buf[term->_bufpos] = '\0';
+	return term->_buf;
+}
+
+#if 0
+char *
+ti_tiparm(TERMINAL *term, const char *str, ...)
+{
+	va_list va;
+	char *ret;
+
+	_DIAGASSERT(term != NULL);
+	_DIAGASSERT(str != NULL);
+
+	va_start(va, str);
+	ret = _ti_tiparm(term, str, VA_CHAR_INT, va);
+	va_end(va);
+	return ret;
+}
+#endif
+
+char *
+compat_tiparm(const char *str, ...)
+{
+	va_list va;
+	char *ret;
+
+	_DIAGASSERT(str != NULL);
+
+	va_start(va, str);
+	ret = _ti_tiparm(NULL, str, VA_CHAR_INT, va);
+	va_end(va);
+	return ret;
+}
+
+#if 0
+#ifdef VA_CHAR_LONG
+char *
+ti_tlparm(TERMINAL *term, const char *str, ...)
+{
+	va_list va;
+	char *ret;
+
+	_DIAGASSERT(term != NULL);
+	_DIAGASSERT(str != NULL);
+
+	va_start(va, str);
+	ret = _ti_tiparm(term, str, VA_CHAR_LONG, va);
+	va_end(va);
+	return ret;
+}
+
+char *
+tlparm(const char *str, ...)
+{
+	va_list va;
+	char *ret;
+
+	_DIAGASSERT(str != NULL);
+
+	va_start(va, str);
+	ret = _ti_tiparm(NULL, str, VA_CHAR_LONG, va);
+	va_end(va);
+	return ret;
+}
+#endif
+
+static char *
+_tparm(const char *str, ...)
+{
+	va_list va;
+	char *ret;
+
+	_DIAGASSERT(str != NULL);
+
+	va_start(va, str);
+	ret = _ti_tiparm(NULL, str, VA_LONG_LONG, va);
+	va_end(va);
+	return ret;
+}
+
+char *
+tparm(const char *str,
+    long p1, long p2, long p3, long p4, long p5,
+    long p6, long p7, long p8, long p9)
+{
+
+	return _tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
+#endif
diff --git a/configure.ac b/configure.ac
index 4b9d75b3..ccbedfee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -351,6 +351,37 @@ else
 	fi
 fi
 
+# Replace tparm on newer ncurses where it has string requirements that are
+# too strict for tmux (requires capabilities to exist).
+AC_MSG_CHECKING(for ncurses with suitable tiparm)
+AC_RUN_IFELSE([AC_LANG_SOURCE(
+	[
+		#include <stdlib.h>
+		#if defined(HAVE_CURSES_H)
+		#include <curses.h>
+		#elif defined(HAVE_NCURSES_H)
+		#include <ncurses.h>
+		#endif
+		int main(void) {
+        #if defined(NCURSES_VERSION_MAJOR) && \
+		    (NCURSES_VERSION_MAJOR > 6 || \
+			(NCURSES_VERSION_MAJOR == 6 && NCURSES_VERSION_MINOR > 3))
+			exit(0);
+		#else
+			exit(1);
+		#endif
+		}
+	])],
+	[
+		AC_MSG_RESULT(no)
+		AC_LIBOBJ(tiparm)
+	],
+	[
+	    AC_MSG_RESULT(yes)
+		AC_REPLACE_FUNCS(tiparm)
+	]
+)
+
 # Look for utempter.
 AC_ARG_ENABLE(
 	utempter,
@@ -438,7 +469,7 @@ fi
 
 # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
 AC_MSG_CHECKING(for b64_ntop)
-	AC_LINK_IFELSE([AC_LANG_PROGRAM(
+AC_LINK_IFELSE([AC_LANG_PROGRAM(
 	[
 		#include <sys/types.h>
 		#include <netinet/in.h>
diff --git a/tty-term.c b/tty-term.c
index 4e9b7799..6fad619f 100644
--- a/tty-term.c
+++ b/tty-term.c
@@ -768,33 +768,78 @@ tty_term_string(struct tty_term *term, enum tty_code_code code)
 const char *
 tty_term_string1(struct tty_term *term, enum tty_code_code code, int a)
 {
-	return (tparm((char *) tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0));
+	const char	*s;
+
+#ifndef HAVE_TIPARM
+	s = compat_tiparm(tty_term_string(term, code), a);
+#else
+	s = tiparm(tty_term_string(term, code), a);
+#endif
+	if (s == NULL)
+		fatalx("could not expand %s", tty_term_codes[code].name);
+	return s;
 }
 
 const char *
 tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b)
 {
-	return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0));
+	const char	*s;
+
+#ifndef HAVE_TIPARM
+	s = compat_tiparm(tty_term_string(term, code), a, b);
+#else
+	s = tiparm(tty_term_string(term, code), a, b);
+#endif
+	if (s == NULL)
+		fatalx("could not expand %s", tty_term_codes[code].name);
+	return s;
 }
 
 const char *
 tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b,
     int c)
 {
-	return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0));
+	const char	*s;
+
+#ifndef HAVE_TIPARM
+	s = compat_tiparm(tty_term_string(term, code), a, b, c);
+#else
+	s = tiparm(tty_term_string(term, code), a, b, c);
+#endif
+	if (s == NULL)
+		fatalx("could not expand %s", tty_term_codes[code].name);
+	return s;
 }
 
 const char *
 tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a)
 {
-	return (tparm((char *) tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0));
+	const char	*s;
+
+#ifndef HAVE_TIPARM
+	s = compat_tiparm(tty_term_string(term, code), a);
+#else
+	s = tiparm(tty_term_string(term, code), a);
+#endif
+	if (s == NULL)
+		fatalx("could not expand %s", tty_term_codes[code].name);
+	return s;
 }
 
 const char *
 tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a,
     const void *b)
 {
-	return (tparm((char *) tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0));
+	const char	*s;
+
+#ifndef HAVE_TIPARM
+	s = compat_tiparm(tty_term_string(term, code), a, b);
+#else
+	s = tiparm(tty_term_string(term, code), a, b);
+#endif
+	if (s == NULL)
+		fatalx("could not expand %s", tty_term_codes[code].name);
+	return s;
 }
 
 int
openSUSE Build Service is sponsored by