150276Speter/****************************************************************************
2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey, 1996 on                                       *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter *	tparm.c
3750276Speter *
3850276Speter */
3950276Speter
4050276Speter#include <curses.priv.h>
4150276Speter
4250276Speter#include <ctype.h>
4350276Speter#include <tic.h>
4450276Speter
45262685SdelphijMODULE_ID("$Id: lib_tparm.c,v 1.90 2013/11/09 14:53:05 tom Exp $")
4650276Speter
4750276Speter/*
4850276Speter *	char *
4950276Speter *	tparm(string, ...)
5050276Speter *
5150276Speter *	Substitute the given parameters into the given string by the following
5250276Speter *	rules (taken from terminfo(5)):
5350276Speter *
5450276Speter *	     Cursor addressing and other strings  requiring  parame-
5550276Speter *	ters in the terminal are described by a parameterized string
56262685Sdelphij *	capability, with escapes like %x in  it.   For  example,  to
5750276Speter *	address  the  cursor, the cup capability is given, using two
5850276Speter *	parameters: the row and column to  address  to.   (Rows  and
5950276Speter *	columns  are  numbered  from  zero and refer to the physical
6050276Speter *	screen visible to the user, not to any  unseen  memory.)  If
6150276Speter *	the terminal has memory relative cursor addressing, that can
6250276Speter *	be indicated by
6350276Speter *
6450276Speter *	     The parameter mechanism uses  a  stack  and  special  %
6550276Speter *	codes  to manipulate it.  Typically a sequence will push one
6650276Speter *	of the parameters onto the stack and then print it  in  some
6750276Speter *	format.  Often more complex operations are necessary.
6850276Speter *
6950276Speter *	     The % encodings have the following meanings:
7050276Speter *
7150276Speter *	     %%        outputs `%'
7250276Speter *	     %c        print pop() like %c in printf()
7350276Speter *	     %s        print pop() like %s in printf()
7450276Speter *           %[[:]flags][width[.precision]][doxXs]
7550276Speter *                     as in printf, flags are [-+#] and space
7666963Speter *                     The ':' is used to avoid making %+ or %-
7766963Speter *                     patterns (see below).
7850276Speter *
7950276Speter *	     %p[1-9]   push ith parm
8050276Speter *	     %P[a-z]   set dynamic variable [a-z] to pop()
8150276Speter *	     %g[a-z]   get dynamic variable [a-z] and push it
8250276Speter *	     %P[A-Z]   set static variable [A-Z] to pop()
8350276Speter *	     %g[A-Z]   get static variable [A-Z] and push it
8450276Speter *	     %l        push strlen(pop)
8550276Speter *	     %'c'      push char constant c
8650276Speter *	     %{nn}     push integer constant nn
8750276Speter *
8850276Speter *	     %+ %- %* %/ %m
8950276Speter *	               arithmetic (%m is mod): push(pop() op pop())
9050276Speter *	     %& %| %^  bit operations: push(pop() op pop())
9150276Speter *	     %= %> %<  logical operations: push(pop() op pop())
9250276Speter *	     %A %O     logical and & or operations for conditionals
9350276Speter *	     %! %~     unary operations push(op pop())
9450276Speter *	     %i        add 1 to first two parms (for ANSI terminals)
9550276Speter *
9650276Speter *	     %? expr %t thenpart %e elsepart %;
9750276Speter *	               if-then-else, %e elsepart is optional.
9850276Speter *	               else-if's are possible ala Algol 68:
9950276Speter *	               %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
10050276Speter *
10150276Speter *	For those of the above operators which are binary and not commutative,
10250276Speter *	the stack works in the usual way, with
10350276Speter *			%gx %gy %m
10450276Speter *	resulting in x mod y, not the reverse.
10550276Speter */
10650276Speter
10776726SpeterNCURSES_EXPORT_VAR(int) _nc_tparm_err = 0;
10876726Speter
109174993Srafan#define TPS(var) _nc_prescreen.tparm_state.var
110262685Sdelphij#define popcount _nc_popcount	/* workaround for NetBSD 6.0 defect */
11166963Speter
11250276Speter#if NO_LEAKS
11376726SpeterNCURSES_EXPORT(void)
11466963Speter_nc_free_tparm(void)
11550276Speter{
116174993Srafan    if (TPS(out_buff) != 0) {
117174993Srafan	FreeAndNull(TPS(out_buff));
118174993Srafan	TPS(out_size) = 0;
119174993Srafan	TPS(out_used) = 0;
120174993Srafan	FreeAndNull(TPS(fmt_buff));
121174993Srafan	TPS(fmt_size) = 0;
12266963Speter    }
12350276Speter}
12450276Speter#endif
12550276Speter
126166124Srafanstatic NCURSES_INLINE void
12766963Speterget_space(size_t need)
12850276Speter{
129174993Srafan    need += TPS(out_used);
130174993Srafan    if (need > TPS(out_size)) {
131174993Srafan	TPS(out_size) = need * 2;
132262685Sdelphij	TYPE_REALLOC(char, TPS(out_size), TPS(out_buff));
133166124Srafan    }
13450276Speter}
13550276Speter
136166124Srafanstatic NCURSES_INLINE void
13766963Spetersave_text(const char *fmt, const char *s, int len)
13850276Speter{
13966963Speter    size_t s_len = strlen(s);
14066963Speter    if (len > (int) s_len)
141262629Sdelphij	s_len = (size_t) len;
14250276Speter
14366963Speter    get_space(s_len + 1);
14450276Speter
145262685Sdelphij    _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
146262685Sdelphij		_nc_SLIMIT(TPS(out_size) - TPS(out_used))
147262685Sdelphij		fmt, s);
148174993Srafan    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
14950276Speter}
15050276Speter
151166124Srafanstatic NCURSES_INLINE void
15266963Spetersave_number(const char *fmt, int number, int len)
15350276Speter{
15466963Speter    if (len < 30)
15566963Speter	len = 30;		/* actually log10(MAX_INT)+1 */
15650276Speter
157262685Sdelphij    get_space((size_t) len + 1);
15850276Speter
159262685Sdelphij    _nc_SPRINTF(TPS(out_buff) + TPS(out_used),
160262685Sdelphij		_nc_SLIMIT(TPS(out_size) - TPS(out_used))
161262685Sdelphij		fmt, number);
162174993Srafan    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
16350276Speter}
16450276Speter
165166124Srafanstatic NCURSES_INLINE void
16666963Spetersave_char(int c)
16750276Speter{
16866963Speter    if (c == 0)
16966963Speter	c = 0200;
170262685Sdelphij    get_space((size_t) 1);
171184989Srafan    TPS(out_buff)[TPS(out_used)++] = (char) c;
17250276Speter}
17350276Speter
174166124Srafanstatic NCURSES_INLINE void
17566963Speternpush(int x)
17650276Speter{
177174993Srafan    if (TPS(stack_ptr) < STACKSIZE) {
178174993Srafan	TPS(stack)[TPS(stack_ptr)].num_type = TRUE;
179174993Srafan	TPS(stack)[TPS(stack_ptr)].data.num = x;
180174993Srafan	TPS(stack_ptr)++;
18176726Speter    } else {
182174993Srafan	DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
18376726Speter	_nc_tparm_err++;
18466963Speter    }
18550276Speter}
18650276Speter
187166124Srafanstatic NCURSES_INLINE int
18866963Speternpop(void)
18950276Speter{
19066963Speter    int result = 0;
191174993Srafan    if (TPS(stack_ptr) > 0) {
192174993Srafan	TPS(stack_ptr)--;
193174993Srafan	if (TPS(stack)[TPS(stack_ptr)].num_type)
194174993Srafan	    result = TPS(stack)[TPS(stack_ptr)].data.num;
19576726Speter    } else {
196174993Srafan	DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
19776726Speter	_nc_tparm_err++;
19866963Speter    }
19966963Speter    return result;
20050276Speter}
20150276Speter
202166124Srafanstatic NCURSES_INLINE void
20366963Speterspush(char *x)
20450276Speter{
205174993Srafan    if (TPS(stack_ptr) < STACKSIZE) {
206174993Srafan	TPS(stack)[TPS(stack_ptr)].num_type = FALSE;
207174993Srafan	TPS(stack)[TPS(stack_ptr)].data.str = x;
208174993Srafan	TPS(stack_ptr)++;
20976726Speter    } else {
210174993Srafan	DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
21176726Speter	_nc_tparm_err++;
21266963Speter    }
21350276Speter}
21450276Speter
215166124Srafanstatic NCURSES_INLINE char *
21666963Speterspop(void)
21750276Speter{
21866963Speter    static char dummy[] = "";	/* avoid const-cast */
21966963Speter    char *result = dummy;
220174993Srafan    if (TPS(stack_ptr) > 0) {
221174993Srafan	TPS(stack_ptr)--;
222174993Srafan	if (!TPS(stack)[TPS(stack_ptr)].num_type
223174993Srafan	    && TPS(stack)[TPS(stack_ptr)].data.str != 0)
224174993Srafan	    result = TPS(stack)[TPS(stack_ptr)].data.str;
22576726Speter    } else {
226174993Srafan	DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
22776726Speter	_nc_tparm_err++;
22866963Speter    }
22966963Speter    return result;
23066963Speter}
23150276Speter
232166124Srafanstatic NCURSES_INLINE const char *
23366963Speterparse_format(const char *s, char *format, int *len)
23466963Speter{
235166124Srafan    *len = 0;
236166124Srafan    if (format != 0) {
237166124Srafan	bool done = FALSE;
238166124Srafan	bool allowminus = FALSE;
239166124Srafan	bool dot = FALSE;
240166124Srafan	bool err = FALSE;
241166124Srafan	char *fmt = format;
242166124Srafan	int my_width = 0;
243166124Srafan	int my_prec = 0;
244166124Srafan	int value = 0;
24566963Speter
246166124Srafan	*len = 0;
247166124Srafan	*format++ = '%';
248166124Srafan	while (*s != '\0' && !done) {
249166124Srafan	    switch (*s) {
250166124Srafan	    case 'c':		/* FALLTHRU */
251166124Srafan	    case 'd':		/* FALLTHRU */
252166124Srafan	    case 'o':		/* FALLTHRU */
253166124Srafan	    case 'x':		/* FALLTHRU */
254166124Srafan	    case 'X':		/* FALLTHRU */
255166124Srafan	    case 's':
256166124Srafan		*format++ = *s;
257166124Srafan		done = TRUE;
258166124Srafan		break;
259166124Srafan	    case '.':
26066963Speter		*format++ = *s++;
261166124Srafan		if (dot) {
26266963Speter		    err = TRUE;
263166124Srafan		} else {	/* value before '.' is the width */
264166124Srafan		    dot = TRUE;
265166124Srafan		    my_width = value;
266166124Srafan		}
267166124Srafan		value = 0;
268166124Srafan		break;
269166124Srafan	    case '#':
27066963Speter		*format++ = *s++;
271166124Srafan		break;
272166124Srafan	    case ' ':
273166124Srafan		*format++ = *s++;
274166124Srafan		break;
275166124Srafan	    case ':':
276166124Srafan		s++;
277166124Srafan		allowminus = TRUE;
278166124Srafan		break;
279166124Srafan	    case '-':
280166124Srafan		if (allowminus) {
281166124Srafan		    *format++ = *s++;
282166124Srafan		} else {
283166124Srafan		    done = TRUE;
284166124Srafan		}
285166124Srafan		break;
286166124Srafan	    default:
287166124Srafan		if (isdigit(UChar(*s))) {
288166124Srafan		    value = (value * 10) + (*s - '0');
289166124Srafan		    if (value > 10000)
290166124Srafan			err = TRUE;
291166124Srafan		    *format++ = *s++;
292166124Srafan		} else {
293166124Srafan		    done = TRUE;
294166124Srafan		}
29566963Speter	    }
29666963Speter	}
29766963Speter
298166124Srafan	/*
299166124Srafan	 * If we found an error, ignore (and remove) the flags.
300166124Srafan	 */
301166124Srafan	if (err) {
302166124Srafan	    my_width = my_prec = value = 0;
303166124Srafan	    format = fmt;
304166124Srafan	    *format++ = '%';
305166124Srafan	    *format++ = *s;
306166124Srafan	}
30766963Speter
308166124Srafan	/*
309166124Srafan	 * Any value after '.' is the precision.  If we did not see '.', then
310166124Srafan	 * the value is the width.
311166124Srafan	 */
312166124Srafan	if (dot)
313166124Srafan	    my_prec = value;
314166124Srafan	else
315166124Srafan	    my_width = value;
31666963Speter
317166124Srafan	*format = '\0';
318166124Srafan	/* return maximum string length in print */
319166124Srafan	*len = (my_width > my_prec) ? my_width : my_prec;
320166124Srafan    }
32166963Speter    return s;
32250276Speter}
32350276Speter
32450276Speter#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
32550276Speter#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
32650276Speter
327166124Srafan/*
328166124Srafan * Analyze the string to see how many parameters we need from the varargs list,
329166124Srafan * and what their types are.  We will only accept string parameters if they
330166124Srafan * appear as a %l or %s format following an explicit parameter reference (e.g.,
331166124Srafan * %p2%s).  All other parameters are numbers.
332166124Srafan *
333166124Srafan * 'number' counts coarsely the number of pop's we see in the string, and
334166124Srafan * 'popcount' shows the highest parameter number in the string.  We would like
335166124Srafan * to simply use the latter count, but if we are reading termcap strings, there
336166124Srafan * may be cases that we cannot see the explicit parameter numbers.
337166124Srafan */
338166124SrafanNCURSES_EXPORT(int)
339166124Srafan_nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
34050276Speter{
341166124Srafan    size_t len2;
342166124Srafan    int i;
343166124Srafan    int lastpop = -1;
34466963Speter    int len;
345166124Srafan    int number = 0;
346166124Srafan    const char *cp = string;
34766963Speter    static char dummy[] = "";
34850276Speter
349166124Srafan    if (cp == 0)
350166124Srafan	return 0;
35150276Speter
352174993Srafan    if ((len2 = strlen(cp)) > TPS(fmt_size)) {
353174993Srafan	TPS(fmt_size) = len2 + TPS(fmt_size) + 2;
354174993Srafan	TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
355174993Srafan	if (TPS(fmt_buff) == 0)
356174993Srafan	    return 0;
35766963Speter    }
35866963Speter
359166124Srafan    memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
360166124Srafan    *popcount = 0;
36166963Speter
362166124Srafan    while ((cp - string) < (int) len2) {
36366963Speter	if (*cp == '%') {
36466963Speter	    cp++;
365174993Srafan	    cp = parse_format(cp, TPS(fmt_buff), &len);
36666963Speter	    switch (*cp) {
36766963Speter	    default:
36866963Speter		break;
36966963Speter
37066963Speter	    case 'd':		/* FALLTHRU */
37166963Speter	    case 'o':		/* FALLTHRU */
37266963Speter	    case 'x':		/* FALLTHRU */
37366963Speter	    case 'X':		/* FALLTHRU */
37466963Speter	    case 'c':		/* FALLTHRU */
375166124Srafan		if (lastpop <= 0)
376166124Srafan		    number++;
37766963Speter		lastpop = -1;
37866963Speter		break;
37966963Speter
38066963Speter	    case 'l':
38166963Speter	    case 's':
38266963Speter		if (lastpop > 0)
38366963Speter		    p_is_s[lastpop - 1] = dummy;
38466963Speter		++number;
38566963Speter		break;
38666963Speter
38766963Speter	    case 'p':
38866963Speter		cp++;
389166124Srafan		i = (UChar(*cp) - '0');
390166124Srafan		if (i >= 0 && i <= NUM_PARM) {
39166963Speter		    lastpop = i;
392166124Srafan		    if (lastpop > *popcount)
393166124Srafan			*popcount = lastpop;
39450276Speter		}
39566963Speter		break;
39650276Speter
39766963Speter	    case 'P':
398166124Srafan		++number;
399166124Srafan		++cp;
400166124Srafan		break;
401166124Srafan
40266963Speter	    case 'g':
40366963Speter		cp++;
40466963Speter		break;
40566963Speter
40666963Speter	    case S_QUOTE:
40766963Speter		cp += 2;
40866963Speter		lastpop = -1;
40966963Speter		break;
41066963Speter
41166963Speter	    case L_BRACE:
41266963Speter		cp++;
413166124Srafan		while (isdigit(UChar(*cp))) {
41466963Speter		    cp++;
41566963Speter		}
41666963Speter		break;
41766963Speter
41866963Speter	    case '+':
41966963Speter	    case '-':
42066963Speter	    case '*':
42166963Speter	    case '/':
42266963Speter	    case 'm':
42366963Speter	    case 'A':
42466963Speter	    case 'O':
42566963Speter	    case '&':
42666963Speter	    case '|':
42766963Speter	    case '^':
42866963Speter	    case '=':
42966963Speter	    case '<':
43066963Speter	    case '>':
431166124Srafan		lastpop = -1;
432166124Srafan		number += 2;
433166124Srafan		break;
434166124Srafan
43566963Speter	    case '!':
43666963Speter	    case '~':
43766963Speter		lastpop = -1;
438166124Srafan		++number;
43966963Speter		break;
44066963Speter
44166963Speter	    case 'i':
442166124Srafan		/* will add 1 to first (usually two) parameters */
44366963Speter		break;
44466963Speter	    }
44550276Speter	}
44666963Speter	if (*cp != '\0')
44766963Speter	    cp++;
44866963Speter    }
44950276Speter
450166124Srafan    if (number > NUM_PARM)
451166124Srafan	number = NUM_PARM;
452166124Srafan    return number;
453166124Srafan}
454166124Srafan
455166124Srafanstatic NCURSES_INLINE char *
456262685Sdelphijtparam_internal(int use_TPARM_ARG, const char *string, va_list ap)
457166124Srafan{
458166124Srafan    char *p_is_s[NUM_PARM];
459166124Srafan    TPARM_ARG param[NUM_PARM];
460262629Sdelphij    int popcount = 0;
461166124Srafan    int number;
462262629Sdelphij    int num_args;
463166124Srafan    int len;
464166124Srafan    int level;
465166124Srafan    int x, y;
466166124Srafan    int i;
467166124Srafan    const char *cp = string;
468166124Srafan    size_t len2;
469166124Srafan
470166124Srafan    if (cp == NULL)
471166124Srafan	return NULL;
472166124Srafan
473174993Srafan    TPS(out_used) = 0;
474166124Srafan    len2 = strlen(cp);
475166124Srafan
476166124Srafan    /*
477166124Srafan     * Find the highest parameter-number referred to in the format string.
478166124Srafan     * Use this value to limit the number of arguments copied from the
479166124Srafan     * variable-length argument list.
480166124Srafan     */
481166124Srafan    number = _nc_tparm_analyze(cp, p_is_s, &popcount);
482174993Srafan    if (TPS(fmt_buff) == 0)
483166124Srafan	return NULL;
484166124Srafan
485262629Sdelphij    if (number > NUM_PARM)
486262629Sdelphij	number = NUM_PARM;
487262629Sdelphij    if (popcount > NUM_PARM)
488262629Sdelphij	popcount = NUM_PARM;
489262629Sdelphij    num_args = max(popcount, number);
490262629Sdelphij
491262629Sdelphij    for (i = 0; i < num_args; i++) {
49250276Speter	/*
49366963Speter	 * A few caps (such as plab_norm) have string-valued parms.
49466963Speter	 * We'll have to assume that the caller knows the difference, since
495166124Srafan	 * a char* and an int may not be the same size on the stack.  The
496166124Srafan	 * normal prototype for this uses 9 long's, which is consistent with
497166124Srafan	 * our va_arg() usage.
49850276Speter	 */
49966963Speter	if (p_is_s[i] != 0) {
50066963Speter	    p_is_s[i] = va_arg(ap, char *);
501262629Sdelphij	    param[i] = 0;
502262629Sdelphij	} else if (use_TPARM_ARG) {
503262629Sdelphij	    param[i] = va_arg(ap, TPARM_ARG);
50466963Speter	} else {
505262629Sdelphij	    param[i] = (TPARM_ARG) va_arg(ap, int);
50650276Speter	}
50766963Speter    }
50850276Speter
50966963Speter    /*
51066963Speter     * This is a termcap compatibility hack.  If there are no explicit pop
51166963Speter     * operations in the string, load the stack in such a way that
51266963Speter     * successive pops will grab successive parameters.  That will make
51366963Speter     * the expansion of (for example) \E[%d;%dH work correctly in termcap
51466963Speter     * style, which means tparam() will expand termcap strings OK.
51566963Speter     */
516174993Srafan    TPS(stack_ptr) = 0;
51766963Speter    if (popcount == 0) {
51866963Speter	popcount = number;
519184989Srafan	for (i = number - 1; i >= 0; i--) {
520184989Srafan	    if (p_is_s[i])
521184989Srafan		spush(p_is_s[i]);
522184989Srafan	    else
523262629Sdelphij		npush((int) param[i]);
524184989Srafan	}
52566963Speter    }
52650276Speter#ifdef TRACE
527174993Srafan    if (USE_TRACEF(TRACE_CALLS)) {
528262685Sdelphij	for (i = 0; i < num_args; i++) {
52966963Speter	    if (p_is_s[i] != 0)
53066963Speter		save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
53166963Speter	    else
532262629Sdelphij		save_number(", %d", (int) param[i], 0);
53350276Speter	}
534174993Srafan	_tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff));
535174993Srafan	TPS(out_used) = 0;
536174993Srafan	_nc_unlock_global(tracef);
53766963Speter    }
53850276Speter#endif /* TRACE */
53950276Speter
540166124Srafan    while ((cp - string) < (int) len2) {
541166124Srafan	if (*cp != '%') {
542166124Srafan	    save_char(UChar(*cp));
54366963Speter	} else {
544174993Srafan	    TPS(tparam_base) = cp++;
545174993Srafan	    cp = parse_format(cp, TPS(fmt_buff), &len);
546166124Srafan	    switch (*cp) {
54766963Speter	    default:
54866963Speter		break;
54966963Speter	    case '%':
55066963Speter		save_char('%');
55166963Speter		break;
55250276Speter
55366963Speter	    case 'd':		/* FALLTHRU */
55466963Speter	    case 'o':		/* FALLTHRU */
55566963Speter	    case 'x':		/* FALLTHRU */
55666963Speter	    case 'X':		/* FALLTHRU */
557174993Srafan		save_number(TPS(fmt_buff), npop(), len);
558166124Srafan		break;
559166124Srafan
56066963Speter	    case 'c':		/* FALLTHRU */
561166124Srafan		save_char(npop());
56266963Speter		break;
56350276Speter
56466963Speter	    case 'l':
565262685Sdelphij		npush((int) strlen(spop()));
56666963Speter		break;
56750276Speter
56866963Speter	    case 's':
569174993Srafan		save_text(TPS(fmt_buff), spop(), len);
57066963Speter		break;
57150276Speter
57266963Speter	    case 'p':
573166124Srafan		cp++;
574166124Srafan		i = (UChar(*cp) - '1');
575166124Srafan		if (i >= 0 && i < NUM_PARM) {
57666963Speter		    if (p_is_s[i])
57766963Speter			spush(p_is_s[i]);
57866963Speter		    else
579262629Sdelphij			npush((int) param[i]);
58066963Speter		}
58166963Speter		break;
58250276Speter
58366963Speter	    case 'P':
584166124Srafan		cp++;
585166124Srafan		if (isUPPER(*cp)) {
586166124Srafan		    i = (UChar(*cp) - 'A');
587174993Srafan		    TPS(static_vars)[i] = npop();
588166124Srafan		} else if (isLOWER(*cp)) {
589166124Srafan		    i = (UChar(*cp) - 'a');
590174993Srafan		    TPS(dynamic_var)[i] = npop();
59166963Speter		}
59266963Speter		break;
59350276Speter
59466963Speter	    case 'g':
595166124Srafan		cp++;
596166124Srafan		if (isUPPER(*cp)) {
597166124Srafan		    i = (UChar(*cp) - 'A');
598174993Srafan		    npush(TPS(static_vars)[i]);
599166124Srafan		} else if (isLOWER(*cp)) {
600166124Srafan		    i = (UChar(*cp) - 'a');
601174993Srafan		    npush(TPS(dynamic_var)[i]);
60266963Speter		}
60366963Speter		break;
60450276Speter
60566963Speter	    case S_QUOTE:
606166124Srafan		cp++;
607166124Srafan		npush(UChar(*cp));
608166124Srafan		cp++;
60966963Speter		break;
61050276Speter
61166963Speter	    case L_BRACE:
61266963Speter		number = 0;
613166124Srafan		cp++;
614166124Srafan		while (isdigit(UChar(*cp))) {
615166124Srafan		    number = (number * 10) + (UChar(*cp) - '0');
616166124Srafan		    cp++;
61766963Speter		}
61866963Speter		npush(number);
61966963Speter		break;
62050276Speter
62166963Speter	    case '+':
62266963Speter		npush(npop() + npop());
62366963Speter		break;
62450276Speter
62566963Speter	    case '-':
62666963Speter		y = npop();
62766963Speter		x = npop();
62866963Speter		npush(x - y);
62966963Speter		break;
63050276Speter
63166963Speter	    case '*':
63266963Speter		npush(npop() * npop());
63366963Speter		break;
63450276Speter
63566963Speter	    case '/':
63666963Speter		y = npop();
63766963Speter		x = npop();
63866963Speter		npush(y ? (x / y) : 0);
63966963Speter		break;
64050276Speter
64166963Speter	    case 'm':
64266963Speter		y = npop();
64366963Speter		x = npop();
64466963Speter		npush(y ? (x % y) : 0);
64566963Speter		break;
64650276Speter
64766963Speter	    case 'A':
64866963Speter		npush(npop() && npop());
64966963Speter		break;
65050276Speter
65166963Speter	    case 'O':
65266963Speter		npush(npop() || npop());
65366963Speter		break;
65450276Speter
65566963Speter	    case '&':
65666963Speter		npush(npop() & npop());
65766963Speter		break;
65850276Speter
65966963Speter	    case '|':
66066963Speter		npush(npop() | npop());
66166963Speter		break;
66250276Speter
66366963Speter	    case '^':
66466963Speter		npush(npop() ^ npop());
66566963Speter		break;
66650276Speter
66766963Speter	    case '=':
66866963Speter		y = npop();
66966963Speter		x = npop();
67066963Speter		npush(x == y);
67166963Speter		break;
67250276Speter
67366963Speter	    case '<':
67466963Speter		y = npop();
67566963Speter		x = npop();
67666963Speter		npush(x < y);
67766963Speter		break;
67850276Speter
67966963Speter	    case '>':
68066963Speter		y = npop();
68166963Speter		x = npop();
68266963Speter		npush(x > y);
68366963Speter		break;
68450276Speter
68566963Speter	    case '!':
68666963Speter		npush(!npop());
68766963Speter		break;
68850276Speter
68966963Speter	    case '~':
69066963Speter		npush(~npop());
69166963Speter		break;
69250276Speter
69366963Speter	    case 'i':
69466963Speter		if (p_is_s[0] == 0)
69566963Speter		    param[0]++;
69666963Speter		if (p_is_s[1] == 0)
69766963Speter		    param[1]++;
69866963Speter		break;
69950276Speter
70066963Speter	    case '?':
70166963Speter		break;
70266963Speter
70366963Speter	    case 't':
70466963Speter		x = npop();
70566963Speter		if (!x) {
70666963Speter		    /* scan forward for %e or %; at level zero */
707166124Srafan		    cp++;
70866963Speter		    level = 0;
709166124Srafan		    while (*cp) {
710166124Srafan			if (*cp == '%') {
711166124Srafan			    cp++;
712166124Srafan			    if (*cp == '?')
71366963Speter				level++;
714166124Srafan			    else if (*cp == ';') {
71566963Speter				if (level > 0)
71666963Speter				    level--;
71766963Speter				else
71866963Speter				    break;
719166124Srafan			    } else if (*cp == 'e' && level == 0)
72050276Speter				break;
72166963Speter			}
72250276Speter
723166124Srafan			if (*cp)
724166124Srafan			    cp++;
72566963Speter		    }
72666963Speter		}
72766963Speter		break;
72850276Speter
72966963Speter	    case 'e':
73066963Speter		/* scan forward for a %; at level zero */
731166124Srafan		cp++;
73266963Speter		level = 0;
733166124Srafan		while (*cp) {
734166124Srafan		    if (*cp == '%') {
735166124Srafan			cp++;
736166124Srafan			if (*cp == '?')
73766963Speter			    level++;
738166124Srafan			else if (*cp == ';') {
73966963Speter			    if (level > 0)
74066963Speter				level--;
74166963Speter			    else
74250276Speter				break;
74366963Speter			}
74466963Speter		    }
74550276Speter
746166124Srafan		    if (*cp)
747166124Srafan			cp++;
74866963Speter		}
74966963Speter		break;
75050276Speter
75166963Speter	    case ';':
75266963Speter		break;
75350276Speter
754166124Srafan	    }			/* endswitch (*cp) */
755166124Srafan	}			/* endelse (*cp == '%') */
75650276Speter
757166124Srafan	if (*cp == '\0')
75866963Speter	    break;
75950276Speter
760166124Srafan	cp++;
761166124Srafan    }				/* endwhile (*cp) */
76250276Speter
763262685Sdelphij    get_space((size_t) 1);
764174993Srafan    TPS(out_buff)[TPS(out_used)] = '\0';
76550276Speter
766174993Srafan    T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
767174993Srafan    return (TPS(out_buff));
76850276Speter}
76950276Speter
770166124Srafan#if NCURSES_TPARM_VARARGS
771166124Srafan#define tparm_varargs tparm
772166124Srafan#else
773166124Srafan#define tparm_proto tparm
774166124Srafan#endif
775166124Srafan
77676726SpeterNCURSES_EXPORT(char *)
777166124Srafantparm_varargs(NCURSES_CONST char *string,...)
77850276Speter{
77966963Speter    va_list ap;
78066963Speter    char *result;
78150276Speter
78276726Speter    _nc_tparm_err = 0;
78366963Speter    va_start(ap, string);
78450276Speter#ifdef TRACE
785174993Srafan    TPS(tname) = "tparm";
78650276Speter#endif /* TRACE */
787262629Sdelphij    result = tparam_internal(TRUE, string, ap);
78866963Speter    va_end(ap);
78966963Speter    return result;
79050276Speter}
791166124Srafan
792166124Srafan#if !NCURSES_TPARM_VARARGS
793166124SrafanNCURSES_EXPORT(char *)
794166124Srafantparm_proto(NCURSES_CONST char *string,
795166124Srafan	    TPARM_ARG a1,
796166124Srafan	    TPARM_ARG a2,
797166124Srafan	    TPARM_ARG a3,
798166124Srafan	    TPARM_ARG a4,
799166124Srafan	    TPARM_ARG a5,
800166124Srafan	    TPARM_ARG a6,
801166124Srafan	    TPARM_ARG a7,
802166124Srafan	    TPARM_ARG a8,
803166124Srafan	    TPARM_ARG a9)
804166124Srafan{
805166124Srafan    return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9);
806166124Srafan}
807166124Srafan#endif /* NCURSES_TPARM_VARARGS */
808262629Sdelphij
809262629SdelphijNCURSES_EXPORT(char *)
810262629Sdelphijtiparm(const char *string,...)
811262629Sdelphij{
812262629Sdelphij    va_list ap;
813262629Sdelphij    char *result;
814262629Sdelphij
815262629Sdelphij    _nc_tparm_err = 0;
816262629Sdelphij    va_start(ap, string);
817262629Sdelphij#ifdef TRACE
818262629Sdelphij    TPS(tname) = "tiparm";
819262629Sdelphij#endif /* TRACE */
820262629Sdelphij    result = tparam_internal(FALSE, string, ap);
821262629Sdelphij    va_end(ap);
822262629Sdelphij    return result;
823262629Sdelphij}
824