150276Speter/****************************************************************************
2184989Srafan * Copyright (c) 1998-2007,2008 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 <term.h>
4450276Speter#include <tic.h>
4550276Speter
46184989SrafanMODULE_ID("$Id: lib_tparm.c,v 1.76 2008/08/16 19:22:55 tom Exp $")
4750276Speter
4850276Speter/*
4950276Speter *	char *
5050276Speter *	tparm(string, ...)
5150276Speter *
5250276Speter *	Substitute the given parameters into the given string by the following
5350276Speter *	rules (taken from terminfo(5)):
5450276Speter *
5550276Speter *	     Cursor addressing and other strings  requiring  parame-
5650276Speter *	ters in the terminal are described by a parameterized string
5750276Speter *	capability, with like escapes %x in  it.   For  example,  to
5850276Speter *	address  the  cursor, the cup capability is given, using two
5950276Speter *	parameters: the row and column to  address  to.   (Rows  and
6050276Speter *	columns  are  numbered  from  zero and refer to the physical
6150276Speter *	screen visible to the user, not to any  unseen  memory.)  If
6250276Speter *	the terminal has memory relative cursor addressing, that can
6350276Speter *	be indicated by
6450276Speter *
6550276Speter *	     The parameter mechanism uses  a  stack  and  special  %
6650276Speter *	codes  to manipulate it.  Typically a sequence will push one
6750276Speter *	of the parameters onto the stack and then print it  in  some
6850276Speter *	format.  Often more complex operations are necessary.
6950276Speter *
7050276Speter *	     The % encodings have the following meanings:
7150276Speter *
7250276Speter *	     %%        outputs `%'
7350276Speter *	     %c        print pop() like %c in printf()
7450276Speter *	     %s        print pop() like %s in printf()
7550276Speter *           %[[:]flags][width[.precision]][doxXs]
7650276Speter *                     as in printf, flags are [-+#] and space
7766963Speter *                     The ':' is used to avoid making %+ or %-
7866963Speter *                     patterns (see below).
7950276Speter *
8050276Speter *	     %p[1-9]   push ith parm
8150276Speter *	     %P[a-z]   set dynamic variable [a-z] to pop()
8250276Speter *	     %g[a-z]   get dynamic variable [a-z] and push it
8350276Speter *	     %P[A-Z]   set static variable [A-Z] to pop()
8450276Speter *	     %g[A-Z]   get static variable [A-Z] and push it
8550276Speter *	     %l        push strlen(pop)
8650276Speter *	     %'c'      push char constant c
8750276Speter *	     %{nn}     push integer constant nn
8850276Speter *
8950276Speter *	     %+ %- %* %/ %m
9050276Speter *	               arithmetic (%m is mod): push(pop() op pop())
9150276Speter *	     %& %| %^  bit operations: push(pop() op pop())
9250276Speter *	     %= %> %<  logical operations: push(pop() op pop())
9350276Speter *	     %A %O     logical and & or operations for conditionals
9450276Speter *	     %! %~     unary operations push(op pop())
9550276Speter *	     %i        add 1 to first two parms (for ANSI terminals)
9650276Speter *
9750276Speter *	     %? expr %t thenpart %e elsepart %;
9850276Speter *	               if-then-else, %e elsepart is optional.
9950276Speter *	               else-if's are possible ala Algol 68:
10050276Speter *	               %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
10150276Speter *
10250276Speter *	For those of the above operators which are binary and not commutative,
10350276Speter *	the stack works in the usual way, with
10450276Speter *			%gx %gy %m
10550276Speter *	resulting in x mod y, not the reverse.
10650276Speter */
10750276Speter
10876726SpeterNCURSES_EXPORT_VAR(int) _nc_tparm_err = 0;
10976726Speter
110174993Srafan#define TPS(var) _nc_prescreen.tparm_state.var
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;
132174993Srafan	TPS(out_buff) = typeRealloc(char, TPS(out_size), TPS(out_buff));
133174993Srafan	if (TPS(out_buff) == 0)
134166124Srafan	    _nc_err_abort(MSG_NO_MEMORY);
135166124Srafan    }
13650276Speter}
13750276Speter
138166124Srafanstatic NCURSES_INLINE void
13966963Spetersave_text(const char *fmt, const char *s, int len)
14050276Speter{
14166963Speter    size_t s_len = strlen(s);
14266963Speter    if (len > (int) s_len)
14366963Speter	s_len = len;
14450276Speter
14566963Speter    get_space(s_len + 1);
14650276Speter
147174993Srafan    (void) sprintf(TPS(out_buff) + TPS(out_used), 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
157166124Srafan    get_space((unsigned) len + 1);
15850276Speter
159174993Srafan    (void) sprintf(TPS(out_buff) + TPS(out_used), fmt, number);
160174993Srafan    TPS(out_used) += strlen(TPS(out_buff) + TPS(out_used));
16150276Speter}
16250276Speter
163166124Srafanstatic NCURSES_INLINE void
16466963Spetersave_char(int c)
16550276Speter{
16666963Speter    if (c == 0)
16766963Speter	c = 0200;
16866963Speter    get_space(1);
169184989Srafan    TPS(out_buff)[TPS(out_used)++] = (char) c;
17050276Speter}
17150276Speter
172166124Srafanstatic NCURSES_INLINE void
17366963Speternpush(int x)
17450276Speter{
175174993Srafan    if (TPS(stack_ptr) < STACKSIZE) {
176174993Srafan	TPS(stack)[TPS(stack_ptr)].num_type = TRUE;
177174993Srafan	TPS(stack)[TPS(stack_ptr)].data.num = x;
178174993Srafan	TPS(stack_ptr)++;
17976726Speter    } else {
180174993Srafan	DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
18176726Speter	_nc_tparm_err++;
18266963Speter    }
18350276Speter}
18450276Speter
185166124Srafanstatic NCURSES_INLINE int
18666963Speternpop(void)
18750276Speter{
18866963Speter    int result = 0;
189174993Srafan    if (TPS(stack_ptr) > 0) {
190174993Srafan	TPS(stack_ptr)--;
191174993Srafan	if (TPS(stack)[TPS(stack_ptr)].num_type)
192174993Srafan	    result = TPS(stack)[TPS(stack_ptr)].data.num;
19376726Speter    } else {
194174993Srafan	DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
19576726Speter	_nc_tparm_err++;
19666963Speter    }
19766963Speter    return result;
19850276Speter}
19950276Speter
200166124Srafanstatic NCURSES_INLINE void
20166963Speterspush(char *x)
20250276Speter{
203174993Srafan    if (TPS(stack_ptr) < STACKSIZE) {
204174993Srafan	TPS(stack)[TPS(stack_ptr)].num_type = FALSE;
205174993Srafan	TPS(stack)[TPS(stack_ptr)].data.str = x;
206174993Srafan	TPS(stack_ptr)++;
20776726Speter    } else {
208174993Srafan	DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base))));
20976726Speter	_nc_tparm_err++;
21066963Speter    }
21150276Speter}
21250276Speter
213166124Srafanstatic NCURSES_INLINE char *
21466963Speterspop(void)
21550276Speter{
21666963Speter    static char dummy[] = "";	/* avoid const-cast */
21766963Speter    char *result = dummy;
218174993Srafan    if (TPS(stack_ptr) > 0) {
219174993Srafan	TPS(stack_ptr)--;
220174993Srafan	if (!TPS(stack)[TPS(stack_ptr)].num_type
221174993Srafan	    && TPS(stack)[TPS(stack_ptr)].data.str != 0)
222174993Srafan	    result = TPS(stack)[TPS(stack_ptr)].data.str;
22376726Speter    } else {
224174993Srafan	DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base))));
22576726Speter	_nc_tparm_err++;
22666963Speter    }
22766963Speter    return result;
22866963Speter}
22950276Speter
230166124Srafanstatic NCURSES_INLINE const char *
23166963Speterparse_format(const char *s, char *format, int *len)
23266963Speter{
233166124Srafan    *len = 0;
234166124Srafan    if (format != 0) {
235166124Srafan	bool done = FALSE;
236166124Srafan	bool allowminus = FALSE;
237166124Srafan	bool dot = FALSE;
238166124Srafan	bool err = FALSE;
239166124Srafan	char *fmt = format;
240166124Srafan	int my_width = 0;
241166124Srafan	int my_prec = 0;
242166124Srafan	int value = 0;
24366963Speter
244166124Srafan	*len = 0;
245166124Srafan	*format++ = '%';
246166124Srafan	while (*s != '\0' && !done) {
247166124Srafan	    switch (*s) {
248166124Srafan	    case 'c':		/* FALLTHRU */
249166124Srafan	    case 'd':		/* FALLTHRU */
250166124Srafan	    case 'o':		/* FALLTHRU */
251166124Srafan	    case 'x':		/* FALLTHRU */
252166124Srafan	    case 'X':		/* FALLTHRU */
253166124Srafan	    case 's':
254166124Srafan		*format++ = *s;
255166124Srafan		done = TRUE;
256166124Srafan		break;
257166124Srafan	    case '.':
25866963Speter		*format++ = *s++;
259166124Srafan		if (dot) {
26066963Speter		    err = TRUE;
261166124Srafan		} else {	/* value before '.' is the width */
262166124Srafan		    dot = TRUE;
263166124Srafan		    my_width = value;
264166124Srafan		}
265166124Srafan		value = 0;
266166124Srafan		break;
267166124Srafan	    case '#':
26866963Speter		*format++ = *s++;
269166124Srafan		break;
270166124Srafan	    case ' ':
271166124Srafan		*format++ = *s++;
272166124Srafan		break;
273166124Srafan	    case ':':
274166124Srafan		s++;
275166124Srafan		allowminus = TRUE;
276166124Srafan		break;
277166124Srafan	    case '-':
278166124Srafan		if (allowminus) {
279166124Srafan		    *format++ = *s++;
280166124Srafan		} else {
281166124Srafan		    done = TRUE;
282166124Srafan		}
283166124Srafan		break;
284166124Srafan	    default:
285166124Srafan		if (isdigit(UChar(*s))) {
286166124Srafan		    value = (value * 10) + (*s - '0');
287166124Srafan		    if (value > 10000)
288166124Srafan			err = TRUE;
289166124Srafan		    *format++ = *s++;
290166124Srafan		} else {
291166124Srafan		    done = TRUE;
292166124Srafan		}
29366963Speter	    }
29466963Speter	}
29566963Speter
296166124Srafan	/*
297166124Srafan	 * If we found an error, ignore (and remove) the flags.
298166124Srafan	 */
299166124Srafan	if (err) {
300166124Srafan	    my_width = my_prec = value = 0;
301166124Srafan	    format = fmt;
302166124Srafan	    *format++ = '%';
303166124Srafan	    *format++ = *s;
304166124Srafan	}
30566963Speter
306166124Srafan	/*
307166124Srafan	 * Any value after '.' is the precision.  If we did not see '.', then
308166124Srafan	 * the value is the width.
309166124Srafan	 */
310166124Srafan	if (dot)
311166124Srafan	    my_prec = value;
312166124Srafan	else
313166124Srafan	    my_width = value;
31466963Speter
315166124Srafan	*format = '\0';
316166124Srafan	/* return maximum string length in print */
317166124Srafan	*len = (my_width > my_prec) ? my_width : my_prec;
318166124Srafan    }
31966963Speter    return s;
32050276Speter}
32150276Speter
32250276Speter#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
32350276Speter#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
32450276Speter
325166124Srafan/*
326166124Srafan * Analyze the string to see how many parameters we need from the varargs list,
327166124Srafan * and what their types are.  We will only accept string parameters if they
328166124Srafan * appear as a %l or %s format following an explicit parameter reference (e.g.,
329166124Srafan * %p2%s).  All other parameters are numbers.
330166124Srafan *
331166124Srafan * 'number' counts coarsely the number of pop's we see in the string, and
332166124Srafan * 'popcount' shows the highest parameter number in the string.  We would like
333166124Srafan * to simply use the latter count, but if we are reading termcap strings, there
334166124Srafan * may be cases that we cannot see the explicit parameter numbers.
335166124Srafan */
336166124SrafanNCURSES_EXPORT(int)
337166124Srafan_nc_tparm_analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
33850276Speter{
339166124Srafan    size_t len2;
340166124Srafan    int i;
341166124Srafan    int lastpop = -1;
34266963Speter    int len;
343166124Srafan    int number = 0;
344166124Srafan    const char *cp = string;
34566963Speter    static char dummy[] = "";
34650276Speter
347166124Srafan    if (cp == 0)
348166124Srafan	return 0;
34950276Speter
350174993Srafan    if ((len2 = strlen(cp)) > TPS(fmt_size)) {
351174993Srafan	TPS(fmt_size) = len2 + TPS(fmt_size) + 2;
352174993Srafan	TPS(fmt_buff) = typeRealloc(char, TPS(fmt_size), TPS(fmt_buff));
353174993Srafan	if (TPS(fmt_buff) == 0)
354174993Srafan	    return 0;
35566963Speter    }
35666963Speter
357166124Srafan    memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
358166124Srafan    *popcount = 0;
35966963Speter
360166124Srafan    while ((cp - string) < (int) len2) {
36166963Speter	if (*cp == '%') {
36266963Speter	    cp++;
363174993Srafan	    cp = parse_format(cp, TPS(fmt_buff), &len);
36466963Speter	    switch (*cp) {
36566963Speter	    default:
36666963Speter		break;
36766963Speter
36866963Speter	    case 'd':		/* FALLTHRU */
36966963Speter	    case 'o':		/* FALLTHRU */
37066963Speter	    case 'x':		/* FALLTHRU */
37166963Speter	    case 'X':		/* FALLTHRU */
37266963Speter	    case 'c':		/* FALLTHRU */
373166124Srafan		if (lastpop <= 0)
374166124Srafan		    number++;
37566963Speter		lastpop = -1;
37666963Speter		break;
37766963Speter
37866963Speter	    case 'l':
37966963Speter	    case 's':
38066963Speter		if (lastpop > 0)
38166963Speter		    p_is_s[lastpop - 1] = dummy;
38266963Speter		++number;
38366963Speter		break;
38466963Speter
38566963Speter	    case 'p':
38666963Speter		cp++;
387166124Srafan		i = (UChar(*cp) - '0');
388166124Srafan		if (i >= 0 && i <= NUM_PARM) {
38966963Speter		    lastpop = i;
390166124Srafan		    if (lastpop > *popcount)
391166124Srafan			*popcount = lastpop;
39250276Speter		}
39366963Speter		break;
39450276Speter
39566963Speter	    case 'P':
396166124Srafan		++number;
397166124Srafan		++cp;
398166124Srafan		break;
399166124Srafan
40066963Speter	    case 'g':
40166963Speter		cp++;
40266963Speter		break;
40366963Speter
40466963Speter	    case S_QUOTE:
40566963Speter		cp += 2;
40666963Speter		lastpop = -1;
40766963Speter		break;
40866963Speter
40966963Speter	    case L_BRACE:
41066963Speter		cp++;
411166124Srafan		while (isdigit(UChar(*cp))) {
41266963Speter		    cp++;
41366963Speter		}
41466963Speter		break;
41566963Speter
41666963Speter	    case '+':
41766963Speter	    case '-':
41866963Speter	    case '*':
41966963Speter	    case '/':
42066963Speter	    case 'm':
42166963Speter	    case 'A':
42266963Speter	    case 'O':
42366963Speter	    case '&':
42466963Speter	    case '|':
42566963Speter	    case '^':
42666963Speter	    case '=':
42766963Speter	    case '<':
42866963Speter	    case '>':
429166124Srafan		lastpop = -1;
430166124Srafan		number += 2;
431166124Srafan		break;
432166124Srafan
43366963Speter	    case '!':
43466963Speter	    case '~':
43566963Speter		lastpop = -1;
436166124Srafan		++number;
43766963Speter		break;
43866963Speter
43966963Speter	    case 'i':
440166124Srafan		/* will add 1 to first (usually two) parameters */
44166963Speter		break;
44266963Speter	    }
44350276Speter	}
44466963Speter	if (*cp != '\0')
44566963Speter	    cp++;
44666963Speter    }
44750276Speter
448166124Srafan    if (number > NUM_PARM)
449166124Srafan	number = NUM_PARM;
450166124Srafan    return number;
451166124Srafan}
452166124Srafan
453166124Srafanstatic NCURSES_INLINE char *
454166124Srafantparam_internal(const char *string, va_list ap)
455166124Srafan{
456166124Srafan    char *p_is_s[NUM_PARM];
457166124Srafan    TPARM_ARG param[NUM_PARM];
458166124Srafan    int popcount;
459166124Srafan    int number;
460166124Srafan    int len;
461166124Srafan    int level;
462166124Srafan    int x, y;
463166124Srafan    int i;
464166124Srafan    const char *cp = string;
465166124Srafan    size_t len2;
466166124Srafan
467166124Srafan    if (cp == NULL)
468166124Srafan	return NULL;
469166124Srafan
470174993Srafan    TPS(out_used) = 0;
471166124Srafan    len2 = strlen(cp);
472166124Srafan
473166124Srafan    /*
474166124Srafan     * Find the highest parameter-number referred to in the format string.
475166124Srafan     * Use this value to limit the number of arguments copied from the
476166124Srafan     * variable-length argument list.
477166124Srafan     */
478166124Srafan    number = _nc_tparm_analyze(cp, p_is_s, &popcount);
479174993Srafan    if (TPS(fmt_buff) == 0)
480166124Srafan	return NULL;
481166124Srafan
48266963Speter    for (i = 0; i < max(popcount, number); i++) {
48350276Speter	/*
48466963Speter	 * A few caps (such as plab_norm) have string-valued parms.
48566963Speter	 * We'll have to assume that the caller knows the difference, since
486166124Srafan	 * a char* and an int may not be the same size on the stack.  The
487166124Srafan	 * normal prototype for this uses 9 long's, which is consistent with
488166124Srafan	 * our va_arg() usage.
48950276Speter	 */
49066963Speter	if (p_is_s[i] != 0) {
49166963Speter	    p_is_s[i] = va_arg(ap, char *);
49266963Speter	} else {
493166124Srafan	    param[i] = va_arg(ap, TPARM_ARG);
49450276Speter	}
49566963Speter    }
49650276Speter
49766963Speter    /*
49866963Speter     * This is a termcap compatibility hack.  If there are no explicit pop
49966963Speter     * operations in the string, load the stack in such a way that
50066963Speter     * successive pops will grab successive parameters.  That will make
50166963Speter     * the expansion of (for example) \E[%d;%dH work correctly in termcap
50266963Speter     * style, which means tparam() will expand termcap strings OK.
50366963Speter     */
504174993Srafan    TPS(stack_ptr) = 0;
50566963Speter    if (popcount == 0) {
50666963Speter	popcount = number;
507184989Srafan	for (i = number - 1; i >= 0; i--) {
508184989Srafan	    if (p_is_s[i])
509184989Srafan		spush(p_is_s[i]);
510184989Srafan	    else
511184989Srafan		npush(param[i]);
512184989Srafan	}
51366963Speter    }
51450276Speter#ifdef TRACE
515174993Srafan    if (USE_TRACEF(TRACE_CALLS)) {
51666963Speter	for (i = 0; i < popcount; i++) {
51766963Speter	    if (p_is_s[i] != 0)
51866963Speter		save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
51966963Speter	    else
52066963Speter		save_number(", %d", param[i], 0);
52150276Speter	}
522174993Srafan	_tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff));
523174993Srafan	TPS(out_used) = 0;
524174993Srafan	_nc_unlock_global(tracef);
52566963Speter    }
52650276Speter#endif /* TRACE */
52750276Speter
528166124Srafan    while ((cp - string) < (int) len2) {
529166124Srafan	if (*cp != '%') {
530166124Srafan	    save_char(UChar(*cp));
53166963Speter	} else {
532174993Srafan	    TPS(tparam_base) = cp++;
533174993Srafan	    cp = parse_format(cp, TPS(fmt_buff), &len);
534166124Srafan	    switch (*cp) {
53566963Speter	    default:
53666963Speter		break;
53766963Speter	    case '%':
53866963Speter		save_char('%');
53966963Speter		break;
54050276Speter
54166963Speter	    case 'd':		/* FALLTHRU */
54266963Speter	    case 'o':		/* FALLTHRU */
54366963Speter	    case 'x':		/* FALLTHRU */
54466963Speter	    case 'X':		/* FALLTHRU */
545174993Srafan		save_number(TPS(fmt_buff), npop(), len);
546166124Srafan		break;
547166124Srafan
54866963Speter	    case 'c':		/* FALLTHRU */
549166124Srafan		save_char(npop());
55066963Speter		break;
55150276Speter
55266963Speter	    case 'l':
553166124Srafan		save_number("%d", (int) strlen(spop()), 0);
55466963Speter		break;
55550276Speter
55666963Speter	    case 's':
557174993Srafan		save_text(TPS(fmt_buff), spop(), len);
55866963Speter		break;
55950276Speter
56066963Speter	    case 'p':
561166124Srafan		cp++;
562166124Srafan		i = (UChar(*cp) - '1');
563166124Srafan		if (i >= 0 && i < NUM_PARM) {
56466963Speter		    if (p_is_s[i])
56566963Speter			spush(p_is_s[i]);
56666963Speter		    else
56766963Speter			npush(param[i]);
56866963Speter		}
56966963Speter		break;
57050276Speter
57166963Speter	    case 'P':
572166124Srafan		cp++;
573166124Srafan		if (isUPPER(*cp)) {
574166124Srafan		    i = (UChar(*cp) - 'A');
575174993Srafan		    TPS(static_vars)[i] = npop();
576166124Srafan		} else if (isLOWER(*cp)) {
577166124Srafan		    i = (UChar(*cp) - 'a');
578174993Srafan		    TPS(dynamic_var)[i] = npop();
57966963Speter		}
58066963Speter		break;
58150276Speter
58266963Speter	    case 'g':
583166124Srafan		cp++;
584166124Srafan		if (isUPPER(*cp)) {
585166124Srafan		    i = (UChar(*cp) - 'A');
586174993Srafan		    npush(TPS(static_vars)[i]);
587166124Srafan		} else if (isLOWER(*cp)) {
588166124Srafan		    i = (UChar(*cp) - 'a');
589174993Srafan		    npush(TPS(dynamic_var)[i]);
59066963Speter		}
59166963Speter		break;
59250276Speter
59366963Speter	    case S_QUOTE:
594166124Srafan		cp++;
595166124Srafan		npush(UChar(*cp));
596166124Srafan		cp++;
59766963Speter		break;
59850276Speter
59966963Speter	    case L_BRACE:
60066963Speter		number = 0;
601166124Srafan		cp++;
602166124Srafan		while (isdigit(UChar(*cp))) {
603166124Srafan		    number = (number * 10) + (UChar(*cp) - '0');
604166124Srafan		    cp++;
60566963Speter		}
60666963Speter		npush(number);
60766963Speter		break;
60850276Speter
60966963Speter	    case '+':
61066963Speter		npush(npop() + npop());
61166963Speter		break;
61250276Speter
61366963Speter	    case '-':
61466963Speter		y = npop();
61566963Speter		x = npop();
61666963Speter		npush(x - y);
61766963Speter		break;
61850276Speter
61966963Speter	    case '*':
62066963Speter		npush(npop() * npop());
62166963Speter		break;
62250276Speter
62366963Speter	    case '/':
62466963Speter		y = npop();
62566963Speter		x = npop();
62666963Speter		npush(y ? (x / y) : 0);
62766963Speter		break;
62850276Speter
62966963Speter	    case 'm':
63066963Speter		y = npop();
63166963Speter		x = npop();
63266963Speter		npush(y ? (x % y) : 0);
63366963Speter		break;
63450276Speter
63566963Speter	    case 'A':
63666963Speter		npush(npop() && npop());
63766963Speter		break;
63850276Speter
63966963Speter	    case 'O':
64066963Speter		npush(npop() || npop());
64166963Speter		break;
64250276Speter
64366963Speter	    case '&':
64466963Speter		npush(npop() & npop());
64566963Speter		break;
64650276Speter
64766963Speter	    case '|':
64866963Speter		npush(npop() | npop());
64966963Speter		break;
65050276Speter
65166963Speter	    case '^':
65266963Speter		npush(npop() ^ npop());
65366963Speter		break;
65450276Speter
65566963Speter	    case '=':
65666963Speter		y = npop();
65766963Speter		x = npop();
65866963Speter		npush(x == y);
65966963Speter		break;
66050276Speter
66166963Speter	    case '<':
66266963Speter		y = npop();
66366963Speter		x = npop();
66466963Speter		npush(x < y);
66566963Speter		break;
66650276Speter
66766963Speter	    case '>':
66866963Speter		y = npop();
66966963Speter		x = npop();
67066963Speter		npush(x > y);
67166963Speter		break;
67250276Speter
67366963Speter	    case '!':
67466963Speter		npush(!npop());
67566963Speter		break;
67650276Speter
67766963Speter	    case '~':
67866963Speter		npush(~npop());
67966963Speter		break;
68050276Speter
68166963Speter	    case 'i':
68266963Speter		if (p_is_s[0] == 0)
68366963Speter		    param[0]++;
68466963Speter		if (p_is_s[1] == 0)
68566963Speter		    param[1]++;
68666963Speter		break;
68750276Speter
68866963Speter	    case '?':
68966963Speter		break;
69066963Speter
69166963Speter	    case 't':
69266963Speter		x = npop();
69366963Speter		if (!x) {
69466963Speter		    /* scan forward for %e or %; at level zero */
695166124Srafan		    cp++;
69666963Speter		    level = 0;
697166124Srafan		    while (*cp) {
698166124Srafan			if (*cp == '%') {
699166124Srafan			    cp++;
700166124Srafan			    if (*cp == '?')
70166963Speter				level++;
702166124Srafan			    else if (*cp == ';') {
70366963Speter				if (level > 0)
70466963Speter				    level--;
70566963Speter				else
70666963Speter				    break;
707166124Srafan			    } else if (*cp == 'e' && level == 0)
70850276Speter				break;
70966963Speter			}
71050276Speter
711166124Srafan			if (*cp)
712166124Srafan			    cp++;
71366963Speter		    }
71466963Speter		}
71566963Speter		break;
71650276Speter
71766963Speter	    case 'e':
71866963Speter		/* scan forward for a %; at level zero */
719166124Srafan		cp++;
72066963Speter		level = 0;
721166124Srafan		while (*cp) {
722166124Srafan		    if (*cp == '%') {
723166124Srafan			cp++;
724166124Srafan			if (*cp == '?')
72566963Speter			    level++;
726166124Srafan			else if (*cp == ';') {
72766963Speter			    if (level > 0)
72866963Speter				level--;
72966963Speter			    else
73050276Speter				break;
73166963Speter			}
73266963Speter		    }
73350276Speter
734166124Srafan		    if (*cp)
735166124Srafan			cp++;
73666963Speter		}
73766963Speter		break;
73850276Speter
73966963Speter	    case ';':
74066963Speter		break;
74150276Speter
742166124Srafan	    }			/* endswitch (*cp) */
743166124Srafan	}			/* endelse (*cp == '%') */
74450276Speter
745166124Srafan	if (*cp == '\0')
74666963Speter	    break;
74750276Speter
748166124Srafan	cp++;
749166124Srafan    }				/* endwhile (*cp) */
75050276Speter
75176726Speter    get_space(1);
752174993Srafan    TPS(out_buff)[TPS(out_used)] = '\0';
75350276Speter
754174993Srafan    T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
755174993Srafan    return (TPS(out_buff));
75650276Speter}
75750276Speter
758166124Srafan#if NCURSES_TPARM_VARARGS
759166124Srafan#define tparm_varargs tparm
760166124Srafan#else
761166124Srafan#define tparm_proto tparm
762166124Srafan#endif
763166124Srafan
76476726SpeterNCURSES_EXPORT(char *)
765166124Srafantparm_varargs(NCURSES_CONST char *string,...)
76650276Speter{
76766963Speter    va_list ap;
76866963Speter    char *result;
76950276Speter
77076726Speter    _nc_tparm_err = 0;
77166963Speter    va_start(ap, string);
77250276Speter#ifdef TRACE
773174993Srafan    TPS(tname) = "tparm";
77450276Speter#endif /* TRACE */
77566963Speter    result = tparam_internal(string, ap);
77666963Speter    va_end(ap);
77766963Speter    return result;
77850276Speter}
779166124Srafan
780166124Srafan#if !NCURSES_TPARM_VARARGS
781166124SrafanNCURSES_EXPORT(char *)
782166124Srafantparm_proto(NCURSES_CONST char *string,
783166124Srafan	    TPARM_ARG a1,
784166124Srafan	    TPARM_ARG a2,
785166124Srafan	    TPARM_ARG a3,
786166124Srafan	    TPARM_ARG a4,
787166124Srafan	    TPARM_ARG a5,
788166124Srafan	    TPARM_ARG a6,
789166124Srafan	    TPARM_ARG a7,
790166124Srafan	    TPARM_ARG a8,
791166124Srafan	    TPARM_ARG a9)
792166124Srafan{
793166124Srafan    return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9);
794166124Srafan}
795166124Srafan#endif /* NCURSES_TPARM_VARARGS */
796