1 /****************************************************************************
2 * Copyright (c) 1998 Free Software Foundation, Inc. *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
31 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
32 ****************************************************************************/
40 #include <curses.priv.h>
44 MODULE_ID("$Id: lib_tparm.c,v 1.22 1998/02/11 12:13:54 tom Exp $")
50 * Substitute the given parameters into the given string by the following
51 * rules (taken from terminfo(5)):
53 * Cursor addressing and other strings requiring parame-
54 * ters in the terminal are described by a parameterized string
55 * capability, with like escapes %x in it. For example, to
56 * address the cursor, the cup capability is given, using two
57 * parameters: the row and column to address to. (Rows and
58 * columns are numbered from zero and refer to the physical
59 * screen visible to the user, not to any unseen memory.) If
60 * the terminal has memory relative cursor addressing, that can
63 * The parameter mechanism uses a stack and special %
64 * codes to manipulate it. Typically a sequence will push one
65 * of the parameters onto the stack and then print it in some
66 * format. Often more complex operations are necessary.
68 * The % encodings have the following meanings:
71 * %d print pop() like %d in printf()
72 * %2d print pop() like %2d in printf()
73 * %02d print pop() like %02d in printf()
74 * %3d print pop() like %3d in printf()
75 * %03d print pop() like %03d in printf()
76 * %2x print pop() like %2x in printf()
77 * %02x print pop() like %02x in printf()
78 * %3x print pop() like %3x in printf()
79 * %03x print pop() like %03x in printf()
80 * %c print pop() like %c in printf()
81 * %s print pop() like %s in printf()
83 * %p[1-9] push ith parm
84 * %P[a-z] set variable [a-z] to pop()
85 * %g[a-z] get variable [a-z] and push it
86 * %'c' push char constant c
87 * %{nn} push integer constant nn
90 * arithmetic (%m is mod): push(pop() op pop())
91 * %& %| %^ bit operations: push(pop() op pop())
92 * %= %> %< logical operations: push(pop() op pop())
93 * %A %O logical and & or operations for conditionals
94 * %! %~ unary operations push(op pop())
95 * %i add 1 to first two parms (for ANSI terminals)
97 * %? expr %t thenpart %e elsepart %;
98 * if-then-else, %e elsepart is optional.
99 * else-if's are possible ala Algol 68:
100 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
102 * For those of the above operators which are binary and not commutative,
103 * the stack works in the usual way, with
105 * resulting in x mod y, not the reverse.
118 static stack_frame stack[STACKSIZE];
119 static int stack_ptr;
121 static const char *tname;
124 static char *out_buff;
125 static size_t out_size;
126 static size_t out_used;
129 void _nc_free_tparm(void)
132 FreeAndNull(out_buff);
139 static void save_text(char *s)
141 size_t want = strlen(s);
142 size_t need = want + out_used + 1;
144 if (need > out_size) {
147 out_buff = malloc(out_size);
149 out_buff = realloc(out_buff, out_size);
151 (void)strcpy(out_buff + out_used, s);
155 static void save_number(const char *fmt, int number)
158 (void)sprintf(temp, fmt, number);
162 static inline void save_char(int c)
171 static inline void npush(int x)
173 if (stack_ptr < STACKSIZE) {
174 stack[stack_ptr].num = x;
179 static inline int npop(void)
181 return (stack_ptr > 0 ? stack[--stack_ptr].num : 0);
184 static inline char *spop(void)
186 return (stack_ptr > 0 ? stack[--stack_ptr].str : 0);
189 static inline char *tparam_internal(const char *string, va_list ap)
194 int variable[NUM_VARS];
201 register const char *cp;
208 * Find the highest parameter-number referred to in the format string.
209 * Use this value to limit the number of arguments copied from the
210 * variable-length argument list.
212 for (cp = string, popcount = number = 0; *cp != '\0'; cp++) {
213 if (cp[0] == '%' && cp[1] != '\0') {
224 if (cp[1] >= '1' && cp[1] <= '9') {
230 case '0': case '1': case '2': case '3': case '4':
231 case '5': case '6': case '7': case '8': case '9':
232 case 'd': case 'c': case 's':
239 if (number > 9) number = 9;
240 for (i = 0; i < max(popcount, number); i++) {
242 * FIXME: potential loss here if sizeof(int) != sizeof(char *).
243 * A few caps (such as plab_norm) have string-valued parms.
245 param[i] = va_arg(ap, int);
249 * This is a termcap compatibility hack. If there are no explicit pop
250 * operations in the string, load the stack in such a way that
251 * successive pops will grab successive parameters. That will make
252 * the expansion of (for example) \E[%d;%dH work correctly in termcap
253 * style, which means tparam() will expand termcap strings OK.
258 for (i = number - 1; i >= 0; i--)
263 if (_nc_tracing & TRACE_CALLS) {
264 for (i = 0; i < popcount; i++)
265 save_number(", %d", param[i]);
266 _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff);
284 save_number("%d", npop());
288 save_number("%x", npop());
294 if (len == '2' || len == '3')
297 if (*string == 'd') {
299 save_number("%02d", npop());
301 save_number("%03d", npop());
303 else if (*string == 'x') {
305 save_number("%02x", npop());
307 save_number("%03x", npop());
314 if (*string == 'd') {
315 save_number("%2d", npop());
317 else if (*string == 'x') {
318 save_number("%2x", npop());
324 if (*string == 'd') {
325 save_number("%3d", npop());
327 else if (*string == 'x') {
328 save_number("%3x", npop());
342 if (*string >= '1' && *string <= '9')
343 npush(param[*string - '1']);
349 if (i >= 0 && i < NUM_VARS) {
351 variable[++varused] = 0;
352 variable[i] = npop();
359 if (i >= 0 && i < NUM_VARS) {
361 variable[++varused] = 0;
375 while (*string >= '0' && *string <= '9') {
376 number = number * 10 + *string - '0';
383 npush(npop() + npop());
393 npush(npop() * npop());
409 npush(npop() && npop());
413 npush(npop() || npop());
417 npush(npop() & npop());
421 npush(npop() | npop());
425 npush(npop() ^ npop());
465 /* scan forward for %e or %; at level zero */
469 if (*string == '%') {
473 else if (*string == ';') {
479 else if (*string == 'e' && level == 0)
490 /* scan forward for a %; at level zero */
494 if (*string == '%') {
498 else if (*string == ';') {
514 } /* endswitch (*string) */
515 } /* endelse (*string == '%') */
521 } /* endwhile (*string) */
524 out_buff = calloc(1,1);
528 T((T_RETURN("%s"), _nc_visbuf(out_buff)));
532 char *tparm(const char *string, ...)
537 va_start(ap, string);
541 result = tparam_internal(string, ap);
546 #ifdef __UNUSED__ /* we never documented this, and it confuses Emacs */
547 char *tparam(const char *string, char *buffer, int bufsiz, ...)
552 va_start(ap, bufsiz);
556 if (tparam_internal(string, ap) != 0
557 && (int)out_used < bufsiz)
558 result = strcpy(buffer, out_buff);
562 #endif /* __UNUSED__ */