1 /****************************************************************************
2 * Copyright (c) 1998-2014,2015 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 * and: Thomas E. Dickey, 1996 on *
33 ****************************************************************************/
40 #include <curses.priv.h>
45 MODULE_ID("$Id: lib_tparm.c,v 1.94 2015/07/17 01:03:35 tom Exp $")
51 * Substitute the given parameters into the given string by the following
52 * rules (taken from terminfo(5)):
54 * Cursor addressing and other strings requiring parame-
55 * ters in the terminal are described by a parameterized string
56 * capability, with escapes like %x in it. For example, to
57 * address the cursor, the cup capability is given, using two
58 * parameters: the row and column to address to. (Rows and
59 * columns are numbered from zero and refer to the physical
60 * screen visible to the user, not to any unseen memory.) If
61 * the terminal has memory relative cursor addressing, that can
64 * The parameter mechanism uses a stack and special %
65 * codes to manipulate it. Typically a sequence will push one
66 * of the parameters onto the stack and then print it in some
67 * format. Often more complex operations are necessary.
69 * The % encodings have the following meanings:
72 * %c print pop() like %c in printf()
73 * %s print pop() like %s in printf()
74 * %[[:]flags][width[.precision]][doxXs]
75 * as in printf, flags are [-+#] and space
76 * The ':' is used to avoid making %+ or %-
77 * patterns (see below).
79 * %p[1-9] push ith parm
80 * %P[a-z] set dynamic variable [a-z] to pop()
81 * %g[a-z] get dynamic variable [a-z] and push it
82 * %P[A-Z] set static variable [A-Z] to pop()
83 * %g[A-Z] get static variable [A-Z] and push it
85 * %'c' push char constant c
86 * %{nn} push integer constant nn
89 * arithmetic (%m is mod): push(pop() op pop())
90 * %& %| %^ bit operations: push(pop() op pop())
91 * %= %> %< logical operations: push(pop() op pop())
92 * %A %O logical and & or operations for conditionals
93 * %! %~ unary operations push(op pop())
94 * %i add 1 to first two parms (for ANSI terminals)
96 * %? expr %t thenpart %e elsepart %;
97 * if-then-else, %e elsepart is optional.
98 * else-if's are possible ala Algol 68:
99 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
101 * For those of the above operators which are binary and not commutative,
102 * the stack works in the usual way, with
104 * resulting in x mod y, not the reverse.
107 NCURSES_EXPORT_VAR(int) _nc_tparm_err
= 0;
109 #define TPS(var) _nc_prescreen.tparm_state.var
110 #define popcount _nc_popcount /* workaround for NetBSD 6.0 defect */
116 if (TPS(out_buff
) != 0) {
117 FreeAndNull(TPS(out_buff
));
120 FreeAndNull(TPS(fmt_buff
));
126 static NCURSES_INLINE
void
127 get_space(size_t need
)
129 need
+= TPS(out_used
);
130 if (need
> TPS(out_size
)) {
131 TPS(out_size
) = need
* 2;
132 TYPE_REALLOC(char, TPS(out_size
), TPS(out_buff
));
136 static NCURSES_INLINE
void
137 save_text(const char *fmt
, const char *s
, int len
)
139 size_t s_len
= strlen(s
);
140 if (len
> (int) s_len
)
141 s_len
= (size_t) len
;
143 get_space(s_len
+ 1);
145 _nc_SPRINTF(TPS(out_buff
) + TPS(out_used
),
146 _nc_SLIMIT(TPS(out_size
) - TPS(out_used
))
148 TPS(out_used
) += strlen(TPS(out_buff
) + TPS(out_used
));
151 static NCURSES_INLINE
void
152 save_number(const char *fmt
, int number
, int len
)
155 len
= 30; /* actually log10(MAX_INT)+1 */
157 get_space((size_t) len
+ 1);
159 _nc_SPRINTF(TPS(out_buff
) + TPS(out_used
),
160 _nc_SLIMIT(TPS(out_size
) - TPS(out_used
))
162 TPS(out_used
) += strlen(TPS(out_buff
) + TPS(out_used
));
165 static NCURSES_INLINE
void
170 get_space((size_t) 1);
171 TPS(out_buff
)[TPS(out_used
)++] = (char) c
;
174 static NCURSES_INLINE
void
177 if (TPS(stack_ptr
) < STACKSIZE
) {
178 TPS(stack
)[TPS(stack_ptr
)].num_type
= TRUE
;
179 TPS(stack
)[TPS(stack_ptr
)].data
.num
= x
;
182 DEBUG(2, ("npush: stack overflow: %s", _nc_visbuf(TPS(tparam_base
))));
187 static NCURSES_INLINE
int
191 if (TPS(stack_ptr
) > 0) {
193 if (TPS(stack
)[TPS(stack_ptr
)].num_type
)
194 result
= TPS(stack
)[TPS(stack_ptr
)].data
.num
;
196 DEBUG(2, ("npop: stack underflow: %s", _nc_visbuf(TPS(tparam_base
))));
202 static NCURSES_INLINE
void
205 if (TPS(stack_ptr
) < STACKSIZE
) {
206 TPS(stack
)[TPS(stack_ptr
)].num_type
= FALSE
;
207 TPS(stack
)[TPS(stack_ptr
)].data
.str
= x
;
210 DEBUG(2, ("spush: stack overflow: %s", _nc_visbuf(TPS(tparam_base
))));
215 static NCURSES_INLINE
char *
218 static char dummy
[] = ""; /* avoid const-cast */
219 char *result
= dummy
;
220 if (TPS(stack_ptr
) > 0) {
222 if (!TPS(stack
)[TPS(stack_ptr
)].num_type
223 && TPS(stack
)[TPS(stack_ptr
)].data
.str
!= 0)
224 result
= TPS(stack
)[TPS(stack_ptr
)].data
.str
;
226 DEBUG(2, ("spop: stack underflow: %s", _nc_visbuf(TPS(tparam_base
))));
232 static NCURSES_INLINE
const char *
233 parse_format(const char *s
, char *format
, int *len
)
238 bool allowminus
= FALSE
;
248 while (*s
!= '\0' && !done
) {
250 case 'c': /* FALLTHRU */
251 case 'd': /* FALLTHRU */
252 case 'o': /* FALLTHRU */
253 case 'x': /* FALLTHRU */
254 case 'X': /* FALLTHRU */
256 #ifdef EXP_XTERM_1005
266 } else { /* value before '.' is the width */
290 if (isdigit(UChar(*s
))) {
291 value
= (value
* 10) + (*s
- '0');
302 * If we found an error, ignore (and remove) the flags.
305 my_width
= my_prec
= value
= 0;
312 * Any value after '.' is the precision. If we did not see '.', then
313 * the value is the width.
321 /* return maximum string length in print */
322 *len
= (my_width
> my_prec
) ? my_width
: my_prec
;
327 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
328 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
331 * Analyze the string to see how many parameters we need from the varargs list,
332 * and what their types are. We will only accept string parameters if they
333 * appear as a %l or %s format following an explicit parameter reference (e.g.,
334 * %p2%s). All other parameters are numbers.
336 * 'number' counts coarsely the number of pop's we see in the string, and
337 * 'popcount' shows the highest parameter number in the string. We would like
338 * to simply use the latter count, but if we are reading termcap strings, there
339 * may be cases that we cannot see the explicit parameter numbers.
342 _nc_tparm_analyze(const char *string
, char *p_is_s
[NUM_PARM
], int *popcount
)
349 const char *cp
= string
;
350 static char dummy
[] = "";
355 if ((len2
= strlen(cp
)) > TPS(fmt_size
)) {
356 TPS(fmt_size
) = len2
+ TPS(fmt_size
) + 2;
357 TPS(fmt_buff
) = typeRealloc(char, TPS(fmt_size
), TPS(fmt_buff
));
358 if (TPS(fmt_buff
) == 0)
362 memset(p_is_s
, 0, sizeof(p_is_s
[0]) * NUM_PARM
);
365 while ((cp
- string
) < (int) len2
) {
368 cp
= parse_format(cp
, TPS(fmt_buff
), &len
);
373 case 'd': /* FALLTHRU */
374 case 'o': /* FALLTHRU */
375 case 'x': /* FALLTHRU */
376 case 'X': /* FALLTHRU */
377 case 'c': /* FALLTHRU */
378 #ifdef EXP_XTERM_1005
389 p_is_s
[lastpop
- 1] = dummy
;
395 i
= (UChar(*cp
) - '0');
396 if (i
>= 0 && i
<= NUM_PARM
) {
398 if (lastpop
> *popcount
)
419 while (isdigit(UChar(*cp
))) {
448 /* will add 1 to first (usually two) parameters */
456 if (number
> NUM_PARM
)
461 static NCURSES_INLINE
char *
462 tparam_internal(int use_TPARM_ARG
, const char *string
, va_list ap
)
464 char *p_is_s
[NUM_PARM
];
465 TPARM_ARG param
[NUM_PARM
];
473 const char *cp
= string
;
476 bool incremented_two
;
485 * Find the highest parameter-number referred to in the format string.
486 * Use this value to limit the number of arguments copied from the
487 * variable-length argument list.
489 number
= _nc_tparm_analyze(cp
, p_is_s
, &popcount
);
490 if (TPS(fmt_buff
) == 0)
493 incremented_two
= FALSE
;
495 if (number
> NUM_PARM
)
497 if (popcount
> NUM_PARM
)
499 num_args
= max(popcount
, number
);
501 for (i
= 0; i
< num_args
; i
++) {
503 * A few caps (such as plab_norm) have string-valued parms.
504 * We'll have to assume that the caller knows the difference, since
505 * a char* and an int may not be the same size on the stack. The
506 * normal prototype for this uses 9 long's, which is consistent with
507 * our va_arg() usage.
509 if (p_is_s
[i
] != 0) {
510 p_is_s
[i
] = va_arg(ap
, char *);
512 } else if (use_TPARM_ARG
) {
513 param
[i
] = va_arg(ap
, TPARM_ARG
);
515 param
[i
] = (TPARM_ARG
) va_arg(ap
, int);
520 * This is a termcap compatibility hack. If there are no explicit pop
521 * operations in the string, load the stack in such a way that
522 * successive pops will grab successive parameters. That will make
523 * the expansion of (for example) \E[%d;%dH work correctly in termcap
524 * style, which means tparam() will expand termcap strings OK.
527 termcap_hack
= FALSE
;
531 for (i
= number
- 1; i
>= 0; i
--) {
535 npush((int) param
[i
]);
539 if (USE_TRACEF(TRACE_CALLS
)) {
540 for (i
= 0; i
< num_args
; i
++) {
542 save_text(", %s", _nc_visbuf(p_is_s
[i
]), 0);
544 save_number(", %d", (int) param
[i
], 0);
546 _tracef(T_CALLED("%s(%s%s)"), TPS(tname
), _nc_visbuf(cp
), TPS(out_buff
));
548 _nc_unlock_global(tracef
);
552 while ((cp
- string
) < (int) len2
) {
554 save_char(UChar(*cp
));
556 TPS(tparam_base
) = cp
++;
557 cp
= parse_format(cp
, TPS(fmt_buff
), &len
);
565 case 'd': /* FALLTHRU */
566 case 'o': /* FALLTHRU */
567 case 'x': /* FALLTHRU */
568 case 'X': /* FALLTHRU */
569 save_number(TPS(fmt_buff
), npop(), len
);
572 case 'c': /* FALLTHRU */
576 #ifdef EXP_XTERM_1005
579 unsigned char target
[10];
580 unsigned source
= (unsigned) npop();
581 int rc
= _nc_conv_to_utf8(target
, source
, (unsigned)
584 for (n
= 0; n
< rc
; ++n
) {
585 save_char(target
[n
]);
591 npush((int) strlen(spop()));
595 save_text(TPS(fmt_buff
), spop(), len
);
600 i
= (UChar(*cp
) - '1');
601 if (i
>= 0 && i
< NUM_PARM
) {
605 npush((int) param
[i
]);
613 i
= (UChar(*cp
) - 'A');
614 TPS(static_vars
)[i
] = npop();
615 } else if (isLOWER(*cp
)) {
616 i
= (UChar(*cp
) - 'a');
617 TPS(dynamic_var
)[i
] = npop();
624 i
= (UChar(*cp
) - 'A');
625 npush(TPS(static_vars
)[i
]);
626 } else if (isLOWER(*cp
)) {
627 i
= (UChar(*cp
) - 'a');
628 npush(TPS(dynamic_var
)[i
]);
641 while (isdigit(UChar(*cp
))) {
642 number
= (number
* 10) + (UChar(*cp
) - '0');
649 npush(npop() + npop());
659 npush(npop() * npop());
665 npush(y
? (x
/ y
) : 0);
671 npush(y
? (x
% y
) : 0);
687 npush(npop() & npop());
691 npush(npop() | npop());
695 npush(npop() ^ npop());
726 * Increment the first two parameters -- if they are numbers
727 * rather than strings. As a side effect, assign into the
728 * stack; if this is termcap, then the stack was populated
729 * using the termcap hack above rather than via the terminfo
732 if (!incremented_two
) {
733 incremented_two
= TRUE
;
734 if (p_is_s
[0] == 0) {
737 TPS(stack
)[0].data
.num
= (int) param
[0];
739 if (p_is_s
[1] == 0) {
742 TPS(stack
)[1].data
.num
= (int) param
[1];
753 /* scan forward for %e or %; at level zero */
761 else if (*cp
== ';') {
766 } else if (*cp
== 'e' && level
== 0)
777 /* scan forward for a %; at level zero */
785 else if (*cp
== ';') {
801 } /* endswitch (*cp) */
802 } /* endelse (*cp == '%') */
808 } /* endwhile (*cp) */
810 get_space((size_t) 1);
811 TPS(out_buff
)[TPS(out_used
)] = '\0';
813 T((T_RETURN("%s"), _nc_visbuf(TPS(out_buff
))));
814 return (TPS(out_buff
));
817 #if NCURSES_TPARM_VARARGS
818 #define tparm_varargs tparm
820 #define tparm_proto tparm
823 NCURSES_EXPORT(char *)
824 tparm_varargs(NCURSES_CONST
char *string
,...)
830 va_start(ap
, string
);
832 TPS(tname
) = "tparm";
834 result
= tparam_internal(TRUE
, string
, ap
);
839 #if !NCURSES_TPARM_VARARGS
840 NCURSES_EXPORT(char *)
841 tparm_proto(NCURSES_CONST
char *string
,
852 return tparm_varargs(string
, a1
, a2
, a3
, a4
, a5
, a6
, a7
, a8
, a9
);
854 #endif /* NCURSES_TPARM_VARARGS */
856 NCURSES_EXPORT(char *)
857 tiparm(const char *string
,...)
863 va_start(ap
, string
);
865 TPS(tname
) = "tiparm";
867 result
= tparam_internal(FALSE
, string
, ap
);