1 /****************************************************************************
2 * Copyright (c) 1998-2003,2004,2005 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 * This code is a modification of lib_tparm.c found in ncurses-5.2. The
31 * modification are for use in grub by replacing all libc function through
32 * special grub functions. This also meant to delete all dynamic memory
33 * allocation and replace it by a number of fixed buffers.
35 * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002
37 * Resync with ncurses-5.4 by Omniflux <omniflux+devel@omniflux.com> 2005
38 **********************************************************************/
40 /****************************************************************************
41 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
42 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
43 * and: Thomas E. Dickey, 1996 on *
44 ****************************************************************************/
51 #include <grub/misc.h>
53 #include <grub/types.h>
54 #include <grub/tparm.h>
57 * Common/troublesome character definitions
59 typedef char grub_bool_t
;
64 # define TRUE (!FALSE)
70 #define MAX_FORMAT_LEN 256
72 #define max(a,b) ((a) > (b) ? (a) : (b))
73 #define isdigit(c) ((c) >= '0' && (c) <= '9')
74 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
75 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
77 #define UChar(c) ((unsigned char)(c))
85 * Substitute the given parameters into the given string by the following
86 * rules (taken from terminfo(5)):
88 * Cursor addressing and other strings requiring parame-
89 * ters in the terminal are described by a parameterized string
90 * capability, with like escapes %x in it. For example, to
91 * address the cursor, the cup capability is given, using two
92 * parameters: the row and column to address to. (Rows and
93 * columns are numbered from zero and refer to the physical
94 * screen visible to the user, not to any unseen memory.) If
95 * the terminal has memory relative cursor addressing, that can
98 * The parameter mechanism uses a stack and special %
99 * codes to manipulate it. Typically a sequence will push one
100 * of the parameters onto the stack and then print it in some
101 * format. Often more complex operations are necessary.
103 * The % encodings have the following meanings:
106 * %c print pop() like %c in printf()
107 * %s print pop() like %s in printf()
108 * %[[:]flags][width[.precision]][doxXs]
109 * as in printf, flags are [-+#] and space
110 * The ':' is used to avoid making %+ or %-
111 * patterns (see below).
113 * %p[1-9] push ith parm
114 * %P[a-z] set dynamic variable [a-z] to pop()
115 * %g[a-z] get dynamic variable [a-z] and push it
116 * %P[A-Z] set static variable [A-Z] to pop()
117 * %g[A-Z] get static variable [A-Z] and push it
118 * %l push strlen(pop)
119 * %'c' push char constant c
120 * %{nn} push integer constant nn
123 * arithmetic (%m is mod): push(pop() op pop())
124 * %& %| %^ bit operations: push(pop() op pop())
125 * %= %> %< logical operations: push(pop() op pop())
126 * %A %O logical and & or operations for conditionals
127 * %! %~ unary operations push(op pop())
128 * %i add 1 to first two parms (for ANSI terminals)
130 * %? expr %t thenpart %e elsepart %;
131 * if-then-else, %e elsepart is optional.
132 * else-if's are possible ala Algol 68:
133 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
135 * For those of the above operators which are binary and not commutative,
136 * the stack works in the usual way, with
138 * resulting in x mod y, not the reverse.
146 grub_bool_t num_type
;
149 static stack_frame stack
[STACKSIZE
];
150 static int stack_ptr
;
151 static const char *tparam_base
= "";
153 static char *out_buff
;
154 static grub_size_t out_size
;
155 static grub_size_t out_used
;
157 static char *fmt_buff
;
158 static grub_size_t fmt_size
;
161 get_space(grub_size_t need
)
164 if (need
> out_size
) {
166 out_buff
= grub_realloc(out_buff
, out_size
*sizeof(char));
167 /* FIX ME! handle out_buff == 0. */
172 save_text(const char *fmt
, const char *s
, int len
)
174 grub_size_t s_len
= grub_strlen(s
);
175 if (len
> (int) s_len
)
178 get_space(s_len
+ 1);
180 (void) grub_sprintf(out_buff
+ out_used
, fmt
, s
);
181 out_used
+= grub_strlen(out_buff
+ out_used
);
185 save_number(const char *fmt
, int number
, int len
)
188 len
= 30; /* actually log10(MAX_INT)+1 */
190 get_space((unsigned) len
+ 1);
192 (void) grub_sprintf(out_buff
+ out_used
, fmt
, number
);
193 out_used
+= grub_strlen(out_buff
+ out_used
);
202 out_buff
[out_used
++] = c
;
208 if (stack_ptr
< STACKSIZE
) {
209 stack
[stack_ptr
].num_type
= TRUE
;
210 stack
[stack_ptr
].data
.num
= x
;
221 if (stack
[stack_ptr
].num_type
)
222 result
= stack
[stack_ptr
].data
.num
;
230 if (stack_ptr
< STACKSIZE
) {
231 stack
[stack_ptr
].num_type
= FALSE
;
232 stack
[stack_ptr
].data
.str
= x
;
240 static char dummy
[] = ""; /* avoid const-cast */
241 char *result
= dummy
;
244 if (!stack
[stack_ptr
].num_type
&& stack
[stack_ptr
].data
.str
!= 0)
245 result
= stack
[stack_ptr
].data
.str
;
250 static inline const char *
251 parse_format(const char *s
, char *format
, int *len
)
255 grub_bool_t done
= FALSE
;
256 grub_bool_t allowminus
= FALSE
;
257 grub_bool_t dot
= FALSE
;
258 grub_bool_t err
= FALSE
;
266 while (*s
!= '\0' && !done
) {
268 case 'c': /* FALLTHRU */
269 case 'd': /* FALLTHRU */
270 case 'o': /* FALLTHRU */
271 case 'x': /* FALLTHRU */
272 case 'X': /* FALLTHRU */
281 } else { /* value before '.' is the width */
305 if (isdigit(UChar(*s
))) {
306 value
= (value
* 10) + (*s
- '0');
317 * If we found an error, ignore (and remove) the flags.
320 my_width
= my_prec
= value
= 0;
327 * Any value after '.' is the precision. If we did not see '.', then
328 * the value is the width.
336 /* return maximum string length in print */
337 *len
= (my_width
> my_prec
) ? my_width
: my_prec
;
343 * Analyze the string to see how many parameters we need from the varargs list,
344 * and what their types are. We will only accept string parameters if they
345 * appear as a %l or %s format following an explicit parameter reference (e.g.,
346 * %p2%s). All other parameters are numbers.
348 * 'number' counts coarsely the number of pop's we see in the string, and
349 * 'popcount' shows the highest parameter number in the string. We would like
350 * to simply use the latter count, but if we are reading termcap strings, there
351 * may be cases that we cannot see the explicit parameter numbers.
354 analyze(const char *string
, char *p_is_s
[NUM_PARM
], int *popcount
)
361 const char *cp
= string
;
362 static char dummy
[] = "";
367 if ((len2
= grub_strlen(cp
)) > fmt_size
) {
368 fmt_size
= len2
+ fmt_size
+ 2;
369 if ((fmt_buff
= grub_realloc(fmt_buff
, fmt_size
*sizeof(char))) == 0)
373 grub_memset(p_is_s
, 0, sizeof(p_is_s
[0]) * NUM_PARM
);
376 while ((cp
- string
) < (int) len2
) {
379 cp
= parse_format(cp
, fmt_buff
, &len
);
384 case 'd': /* FALLTHRU */
385 case 'o': /* FALLTHRU */
386 case 'x': /* FALLTHRU */
387 case 'X': /* FALLTHRU */
388 case 'c': /* FALLTHRU */
397 p_is_s
[lastpop
- 1] = dummy
;
403 i
= (UChar(*cp
) - '0');
404 if (i
>= 0 && i
<= NUM_PARM
) {
406 if (lastpop
> *popcount
)
427 while (isdigit(UChar(*cp
))) {
456 /* will add 1 to first (usually two) parameters */
464 if (number
> NUM_PARM
)
470 tparam_internal(const char *string
, va_list ap
)
472 char *p_is_s
[NUM_PARM
];
473 long param
[NUM_PARM
];
480 const char *cp
= string
;
482 static int dynamic_var
[NUM_VARS
];
483 static int static_vars
[NUM_VARS
];
488 out_used
= out_size
= fmt_size
= 0;
490 len2
= (int) grub_strlen(cp
);
493 * Find the highest parameter-number referred to in the format string.
494 * Use this value to limit the number of arguments copied from the
495 * variable-length argument list.
497 number
= analyze(cp
, p_is_s
, &popcount
);
501 for (i
= 0; i
< max(popcount
, number
); 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.
507 if (p_is_s
[i
] != 0) {
508 p_is_s
[i
] = va_arg(ap
, char *);
510 param
[i
] = va_arg(ap
, long int);
515 * This is a termcap compatibility hack. If there are no explicit pop
516 * operations in the string, load the stack in such a way that
517 * successive pops will grab successive parameters. That will make
518 * the expansion of (for example) \E[%d;%dH work correctly in termcap
519 * style, which means tparam() will expand termcap strings OK.
524 for (i
= number
- 1; i
>= 0; i
--)
528 while ((cp
- string
) < (int) len2
) {
530 save_char(UChar(*cp
));
533 cp
= parse_format(cp
, fmt_buff
, &len
);
541 case 'd': /* FALLTHRU */
542 case 'o': /* FALLTHRU */
543 case 'x': /* FALLTHRU */
544 case 'X': /* FALLTHRU */
545 save_number(fmt_buff
, npop(), len
);
548 case 'c': /* FALLTHRU */
553 save_number("%d", (int) grub_strlen(spop()), 0);
557 save_text(fmt_buff
, spop(), len
);
562 i
= (UChar(*cp
) - '1');
563 if (i
>= 0 && i
< NUM_PARM
) {
574 i
= (UChar(*cp
) - 'A');
575 static_vars
[i
] = npop();
576 } else if (isLOWER(*cp
)) {
577 i
= (UChar(*cp
) - 'a');
578 dynamic_var
[i
] = npop();
585 i
= (UChar(*cp
) - 'A');
586 npush(static_vars
[i
]);
587 } else if (isLOWER(*cp
)) {
588 i
= (UChar(*cp
) - 'a');
589 npush(dynamic_var
[i
]);
602 while (isdigit(UChar(*cp
))) {
603 number
= (number
* 10) + (UChar(*cp
) - '0');
610 npush(npop() + npop());
620 npush(npop() * npop());
626 npush(y
? (x
/ y
) : 0);
632 npush(y
? (x
% y
) : 0);
636 npush(npop() && npop());
640 npush(npop() || npop());
644 npush(npop() & npop());
648 npush(npop() | npop());
652 npush(npop() ^ npop());
694 /* scan forward for %e or %; at level zero */
702 else if (*cp
== ';') {
707 } else if (*cp
== 'e' && level
== 0)
718 /* scan forward for a %; at level zero */
726 else if (*cp
== ';') {
742 } /* endswitch (*cp) */
743 } /* endelse (*cp == '%') */
749 } /* endwhile (*cp) */
752 out_buff
[out_used
] = '\0';
758 grub_terminfo_tparm (const char *string
, ...)
763 va_start (ap
, string
);
764 result
= tparam_internal (string
, ap
);