lib_tparm.c revision 174993
150276Speter/****************************************************************************
2174993Srafan * Copyright (c) 1998-2006,2007 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
46174993SrafanMODULE_ID("$Id: lib_tparm.c,v 1.74 2007/09/29 20:37:13 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);
169174993Srafan    TPS(out_buff)[TPS(out_used)++] = 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;
50766963Speter	for (i = number - 1; i >= 0; i--)
50866963Speter	    npush(param[i]);
50966963Speter    }
51050276Speter#ifdef TRACE
511174993Srafan    if (USE_TRACEF(TRACE_CALLS)) {
51266963Speter	for (i = 0; i < popcount; i++) {
51366963Speter	    if (p_is_s[i] != 0)
51466963Speter		save_text(", %s", _nc_visbuf(p_is_s[i]), 0);
51566963Speter	    else
51666963Speter		save_number(", %d", param[i], 0);
51750276Speter	}
518174993Srafan	_tracef(T_CALLED("%s(%s%s)"), TPS(tname), _nc_visbuf(cp), TPS(out_buff));
519174993Srafan	TPS(out_used) = 0;
520174993Srafan	_nc_unlock_global(tracef);
52166963Speter    }
52250276Speter#endif /* TRACE */
52350276Speter
524166124Srafan    while ((cp - string) < (int) len2) {
525166124Srafan	if (*cp != '%') {
526166124Srafan	    save_char(UChar(*cp));
52766963Speter	} else {
528174993Srafan	    TPS(tparam_base) = cp++;
529174993Srafan	    cp = parse_format(cp, TPS(fmt_buff), &len);
530166124Srafan	    switch (*cp) {
53166963Speter	    default:
53266963Speter		break;
53366963Speter	    case '%':
53466963Speter		save_char('%');
53566963Speter		break;
53650276Speter
53766963Speter	    case 'd':		/* FALLTHRU */
53866963Speter	    case 'o':		/* FALLTHRU */
53966963Speter	    case 'x':		/* FALLTHRU */
54066963Speter	    case 'X':		/* FALLTHRU */
541174993Srafan		save_number(TPS(fmt_buff), npop(), len);
542166124Srafan		break;
543166124Srafan
54466963Speter	    case 'c':		/* FALLTHRU */
545166124Srafan		save_char(npop());
54666963Speter		break;
54750276Speter
54866963Speter	    case 'l':
549166124Srafan		save_number("%d", (int) strlen(spop()), 0);
55066963Speter		break;
55150276Speter
55266963Speter	    case 's':
553174993Srafan		save_text(TPS(fmt_buff), spop(), len);
55466963Speter		break;
55550276Speter
55666963Speter	    case 'p':
557166124Srafan		cp++;
558166124Srafan		i = (UChar(*cp) - '1');
559166124Srafan		if (i >= 0 && i < NUM_PARM) {
56066963Speter		    if (p_is_s[i])
56166963Speter			spush(p_is_s[i]);
56266963Speter		    else
56366963Speter			npush(param[i]);
56466963Speter		}
56566963Speter		break;
56650276Speter
56766963Speter	    case 'P':
568166124Srafan		cp++;
569166124Srafan		if (isUPPER(*cp)) {
570166124Srafan		    i = (UChar(*cp) - 'A');
571174993Srafan		    TPS(static_vars)[i] = npop();
572166124Srafan		} else if (isLOWER(*cp)) {
573166124Srafan		    i = (UChar(*cp) - 'a');
574174993Srafan		    TPS(dynamic_var)[i] = npop();
57566963Speter		}
57666963Speter		break;
57750276Speter
57866963Speter	    case 'g':
579166124Srafan		cp++;
580166124Srafan		if (isUPPER(*cp)) {
581166124Srafan		    i = (UChar(*cp) - 'A');
582174993Srafan		    npush(TPS(static_vars)[i]);
583166124Srafan		} else if (isLOWER(*cp)) {
584166124Srafan		    i = (UChar(*cp) - 'a');
585174993Srafan		    npush(TPS(dynamic_var)[i]);
58666963Speter		}
58766963Speter		break;
58850276Speter
58966963Speter	    case S_QUOTE:
590166124Srafan		cp++;
591166124Srafan		npush(UChar(*cp));
592166124Srafan		cp++;
59366963Speter		break;
59450276Speter
59566963Speter	    case L_BRACE:
59666963Speter		number = 0;
597166124Srafan		cp++;
598166124Srafan		while (isdigit(UChar(*cp))) {
599166124Srafan		    number = (number * 10) + (UChar(*cp) - '0');
600166124Srafan		    cp++;
60166963Speter		}
60266963Speter		npush(number);
60366963Speter		break;
60450276Speter
60566963Speter	    case '+':
60666963Speter		npush(npop() + npop());
60766963Speter		break;
60850276Speter
60966963Speter	    case '-':
61066963Speter		y = npop();
61166963Speter		x = npop();
61266963Speter		npush(x - y);
61366963Speter		break;
61450276Speter
61566963Speter	    case '*':
61666963Speter		npush(npop() * npop());
61766963Speter		break;
61850276Speter
61966963Speter	    case '/':
62066963Speter		y = npop();
62166963Speter		x = npop();
62266963Speter		npush(y ? (x / y) : 0);
62366963Speter		break;
62450276Speter
62566963Speter	    case 'm':
62666963Speter		y = npop();
62766963Speter		x = npop();
62866963Speter		npush(y ? (x % y) : 0);
62966963Speter		break;
63050276Speter
63166963Speter	    case 'A':
63266963Speter		npush(npop() && npop());
63366963Speter		break;
63450276Speter
63566963Speter	    case 'O':
63666963Speter		npush(npop() || npop());
63766963Speter		break;
63850276Speter
63966963Speter	    case '&':
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		y = npop();
65366963Speter		x = npop();
65466963Speter		npush(x == y);
65566963Speter		break;
65650276Speter
65766963Speter	    case '<':
65866963Speter		y = npop();
65966963Speter		x = npop();
66066963Speter		npush(x < y);
66166963Speter		break;
66250276Speter
66366963Speter	    case '>':
66466963Speter		y = npop();
66566963Speter		x = npop();
66666963Speter		npush(x > y);
66766963Speter		break;
66850276Speter
66966963Speter	    case '!':
67066963Speter		npush(!npop());
67166963Speter		break;
67250276Speter
67366963Speter	    case '~':
67466963Speter		npush(~npop());
67566963Speter		break;
67650276Speter
67766963Speter	    case 'i':
67866963Speter		if (p_is_s[0] == 0)
67966963Speter		    param[0]++;
68066963Speter		if (p_is_s[1] == 0)
68166963Speter		    param[1]++;
68266963Speter		break;
68350276Speter
68466963Speter	    case '?':
68566963Speter		break;
68666963Speter
68766963Speter	    case 't':
68866963Speter		x = npop();
68966963Speter		if (!x) {
69066963Speter		    /* scan forward for %e or %; at level zero */
691166124Srafan		    cp++;
69266963Speter		    level = 0;
693166124Srafan		    while (*cp) {
694166124Srafan			if (*cp == '%') {
695166124Srafan			    cp++;
696166124Srafan			    if (*cp == '?')
69766963Speter				level++;
698166124Srafan			    else if (*cp == ';') {
69966963Speter				if (level > 0)
70066963Speter				    level--;
70166963Speter				else
70266963Speter				    break;
703166124Srafan			    } else if (*cp == 'e' && level == 0)
70450276Speter				break;
70566963Speter			}
70650276Speter
707166124Srafan			if (*cp)
708166124Srafan			    cp++;
70966963Speter		    }
71066963Speter		}
71166963Speter		break;
71250276Speter
71366963Speter	    case 'e':
71466963Speter		/* scan forward for a %; at level zero */
715166124Srafan		cp++;
71666963Speter		level = 0;
717166124Srafan		while (*cp) {
718166124Srafan		    if (*cp == '%') {
719166124Srafan			cp++;
720166124Srafan			if (*cp == '?')
72166963Speter			    level++;
722166124Srafan			else if (*cp == ';') {
72366963Speter			    if (level > 0)
72466963Speter				level--;
72566963Speter			    else
72650276Speter				break;
72766963Speter			}
72866963Speter		    }
72950276Speter
730166124Srafan		    if (*cp)
731166124Srafan			cp++;
73266963Speter		}
73366963Speter		break;
73450276Speter
73566963Speter	    case ';':
73666963Speter		break;
73750276Speter
738166124Srafan	    }			/* endswitch (*cp) */
739166124Srafan	}			/* endelse (*cp == '%') */
74050276Speter
741166124Srafan	if (*cp == '\0')
74266963Speter	    break;
74350276Speter
744166124Srafan	cp++;
745166124Srafan    }				/* endwhile (*cp) */
74650276Speter
74776726Speter    get_space(1);
748174993Srafan    TPS(out_buff)[TPS(out_used)] = '\0';
74950276Speter
750174993Srafan    T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff))));
751174993Srafan    return (TPS(out_buff));
75250276Speter}
75350276Speter
754166124Srafan#if NCURSES_TPARM_VARARGS
755166124Srafan#define tparm_varargs tparm
756166124Srafan#else
757166124Srafan#define tparm_proto tparm
758166124Srafan#endif
759166124Srafan
76076726SpeterNCURSES_EXPORT(char *)
761166124Srafantparm_varargs(NCURSES_CONST char *string,...)
76250276Speter{
76366963Speter    va_list ap;
76466963Speter    char *result;
76550276Speter
76676726Speter    _nc_tparm_err = 0;
76766963Speter    va_start(ap, string);
76850276Speter#ifdef TRACE
769174993Srafan    TPS(tname) = "tparm";
77050276Speter#endif /* TRACE */
77166963Speter    result = tparam_internal(string, ap);
77266963Speter    va_end(ap);
77366963Speter    return result;
77450276Speter}
775166124Srafan
776166124Srafan#if !NCURSES_TPARM_VARARGS
777166124SrafanNCURSES_EXPORT(char *)
778166124Srafantparm_proto(NCURSES_CONST char *string,
779166124Srafan	    TPARM_ARG a1,
780166124Srafan	    TPARM_ARG a2,
781166124Srafan	    TPARM_ARG a3,
782166124Srafan	    TPARM_ARG a4,
783166124Srafan	    TPARM_ARG a5,
784166124Srafan	    TPARM_ARG a6,
785166124Srafan	    TPARM_ARG a7,
786166124Srafan	    TPARM_ARG a8,
787166124Srafan	    TPARM_ARG a9)
788166124Srafan{
789166124Srafan    return tparm_varargs(string, a1, a2, a3, a4, a5, a6, a7, a8, a9);
790166124Srafan}
791166124Srafan#endif /* NCURSES_TPARM_VARARGS */
792