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>
46 MODULE_ID("$Id: lib_tparm.c,v 1.39 1999/06/06 00:04:55 tom Exp $")
52 * Substitute the given parameters into the given string by the following
53 * rules (taken from terminfo(5)):
55 * Cursor addressing and other strings requiring parame-
56 * ters in the terminal are described by a parameterized string
57 * capability, with like escapes %x in it. For example, to
58 * address the cursor, the cup capability is given, using two
59 * parameters: the row and column to address to. (Rows and
60 * columns are numbered from zero and refer to the physical
61 * screen visible to the user, not to any unseen memory.) If
62 * the terminal has memory relative cursor addressing, that can
65 * The parameter mechanism uses a stack and special %
66 * codes to manipulate it. Typically a sequence will push one
67 * of the parameters onto the stack and then print it in some
68 * format. Often more complex operations are necessary.
70 * The % encodings have the following meanings:
73 * %c print pop() like %c in printf()
74 * %s print pop() like %s in printf()
75 * %[[:]flags][width[.precision]][doxXs]
76 * as in printf, flags are [-+#] and space
78 * %p[1-9] push ith parm
79 * %P[a-z] set dynamic variable [a-z] to pop()
80 * %g[a-z] get dynamic variable [a-z] and push it
81 * %P[A-Z] set static variable [A-Z] to pop()
82 * %g[A-Z] get static variable [A-Z] and push it
84 * %'c' push char constant c
85 * %{nn} push integer constant nn
88 * arithmetic (%m is mod): push(pop() op pop())
89 * %& %| %^ bit operations: push(pop() op pop())
90 * %= %> %< logical operations: push(pop() op pop())
91 * %A %O logical and & or operations for conditionals
92 * %! %~ unary operations push(op pop())
93 * %i add 1 to first two parms (for ANSI terminals)
95 * %? expr %t thenpart %e elsepart %;
96 * if-then-else, %e elsepart is optional.
97 * else-if's are possible ala Algol 68:
98 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
100 * For those of the above operators which are binary and not commutative,
101 * the stack works in the usual way, with
103 * resulting in x mod y, not the reverse.
113 static stack_frame stack[STACKSIZE];
114 static int stack_ptr;
116 static const char *tname;
119 static char *out_buff;
120 static size_t out_size;
121 static size_t out_used;
124 void _nc_free_tparm(void)
127 FreeAndNull(out_buff);
134 static void really_get_space(size_t need)
137 out_buff = typeRealloc(char, out_size, out_buff);
139 _nc_err_abort("Out of memory");
142 static inline void get_space(size_t need)
146 really_get_space(need);
149 static inline void save_text(const char *fmt, char *s, int len)
151 size_t s_len = strlen(s);
152 if (len > (int)s_len)
155 get_space(s_len + 1);
157 (void)sprintf(out_buff+out_used, fmt, s);
158 out_used += strlen(out_buff+out_used);
161 static inline void save_number(const char *fmt, int number, int len)
164 len = 30; /* actually log10(MAX_INT)+1 */
168 (void)sprintf(out_buff+out_used, fmt, number);
169 out_used += strlen(out_buff+out_used);
172 static inline void save_char(int c)
177 out_buff[out_used++] = c;
180 static inline void npush(int x)
182 if (stack_ptr < STACKSIZE) {
183 stack[stack_ptr].num = x;
188 static inline int npop(void)
190 return (stack_ptr > 0 ? stack[--stack_ptr].num : 0);
193 static inline char *spop(void)
195 static char dummy[] = ""; /* avoid const-cast */
196 return (stack_ptr > 0 ? stack[--stack_ptr].str : dummy);
199 static inline const char *parse_format(const char *s, char *format, int *len)
202 bool allowminus = FALSE;
209 while (*s != '\0' && !done) {
211 case 'c': /* FALLTHRU */
212 case 'd': /* FALLTHRU */
213 case 'o': /* FALLTHRU */
214 case 'x': /* FALLTHRU */
215 case 'X': /* FALLTHRU */
244 prec = (prec * 10) + (*s - '0');
246 width = (width * 10) + (*s - '0');
254 /* return maximum string length in print */
255 *len = (prec > width) ? prec : width;
259 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
260 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
262 static inline char *tparam_internal(const char *string, va_list ap)
272 register const char *cp;
273 static size_t len_fmt;
275 static int dynamic_var[NUM_VARS];
276 static int static_vars[NUM_VARS];
283 * Find the highest parameter-number referred to in the format string.
284 * Use this value to limit the number of arguments copied from the
285 * variable-length argument list.
287 for (cp = string, popcount = number = 0; *cp != '\0'; cp++) {
288 if (cp[0] == '%' && cp[1] != '\0') {
299 if (cp[1] >= '1' && cp[1] <= '9') {
305 case '0': case '1': case '2': case '3': case '4':
306 case '5': case '6': case '7': case '8': case '9':
307 case 'd': case 'c': case 's':
313 if ((size_t)(cp - string) > len_fmt) {
314 len_fmt = (cp - string) + len_fmt + 2;
315 if ((format = typeRealloc(char, len_fmt, format)) == 0)
319 if (number > 9) number = 9;
320 for (i = 0; i < max(popcount, number); i++) {
322 * FIXME: potential loss here if sizeof(int) != sizeof(char *).
323 * A few caps (such as plab_norm) have string-valued parms.
325 param[i] = va_arg(ap, int);
329 * This is a termcap compatibility hack. If there are no explicit pop
330 * operations in the string, load the stack in such a way that
331 * successive pops will grab successive parameters. That will make
332 * the expansion of (for example) \E[%d;%dH work correctly in termcap
333 * style, which means tparam() will expand termcap strings OK.
338 for (i = number - 1; i >= 0; i--)
343 if (_nc_tracing & TRACE_CALLS) {
344 for (i = 0; i < popcount; i++)
345 save_number(", %d", param[i], 0);
346 _tracef(T_CALLED("%s(%s%s)"), tname, _nc_visbuf(string), out_buff);
352 if (*string != '%') {
356 string = parse_format(string, format, &len);
364 case 'd': /* FALLTHRU */
365 case 'o': /* FALLTHRU */
366 case 'x': /* FALLTHRU */
367 case 'X': /* FALLTHRU */
369 save_number(format, npop(), len);
373 save_number("%d", strlen(spop()), 0);
377 save_text(format, spop(), len);
382 if (*string >= '1' && *string <= '9')
383 npush(param[*string - '1']);
388 if (isUPPER(*string)) {
390 static_vars[i] = npop();
391 } else if (isLOWER(*string)) {
393 dynamic_var[i] = npop();
399 if (isUPPER(*string)) {
401 npush(static_vars[i]);
402 } else if (isLOWER(*string)) {
404 npush(dynamic_var[i]);
417 while (*string >= '0' && *string <= '9') {
418 number = number * 10 + *string - '0';
425 npush(npop() + npop());
435 npush(npop() * npop());
441 npush(y ? (x / y) : 0);
447 npush(y ? (x % y) : 0);
451 npush(npop() && npop());
455 npush(npop() || npop());
459 npush(npop() & npop());
463 npush(npop() | npop());
467 npush(npop() ^ npop());
507 /* scan forward for %e or %; at level zero */
511 if (*string == '%') {
515 else if (*string == ';') {
521 else if (*string == 'e' && level == 0)
532 /* scan forward for a %; at level zero */
536 if (*string == '%') {
540 else if (*string == ';') {
556 } /* endswitch (*string) */
557 } /* endelse (*string == '%') */
563 } /* endwhile (*string) */
565 if (out_buff == 0 && (out_buff = typeCalloc(char,1)) == NULL)
567 out_buff[out_used] = '\0';
569 T((T_RETURN("%s"), _nc_visbuf(out_buff)));
573 char *tparm(NCURSES_CONST char *string, ...)
578 va_start(ap, string);
582 result = tparam_internal(string, ap);