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
[] = "";
369 if ((len2
= grub_strlen(cp
)) > fmt_size
) {
370 fmt_size
= len2
+ fmt_size
+ 2;
371 if ((fmt_buff
= grub_realloc(fmt_buff
, fmt_size
*sizeof(char))) == 0)
375 grub_memset(p_is_s
, 0, sizeof(p_is_s
[0]) * NUM_PARM
);
377 while ((cp
- string
) < (int) len2
) {
380 cp
= parse_format(cp
, fmt_buff
, &len
);
385 case 'd': /* FALLTHRU */
386 case 'o': /* FALLTHRU */
387 case 'x': /* FALLTHRU */
388 case 'X': /* FALLTHRU */
389 case 'c': /* FALLTHRU */
398 p_is_s
[lastpop
- 1] = dummy
;
404 i
= (UChar(*cp
) - '0');
405 if (i
>= 0 && i
<= NUM_PARM
) {
407 if (lastpop
> *popcount
)
428 while (isdigit(UChar(*cp
))) {
457 /* will add 1 to first (usually two) parameters */
465 if (number
> NUM_PARM
)
471 tparam_internal(const char *string
, va_list ap
)
473 char *p_is_s
[NUM_PARM
];
474 long param
[NUM_PARM
];
481 const char *cp
= string
;
483 static int dynamic_var
[NUM_VARS
];
484 static int static_vars
[NUM_VARS
];
489 out_used
= out_size
= fmt_size
= 0;
491 len2
= (int) grub_strlen(cp
);
494 * Find the highest parameter-number referred to in the format string.
495 * Use this value to limit the number of arguments copied from the
496 * variable-length argument list.
498 number
= analyze(cp
, p_is_s
, &popcount
);
502 for (i
= 0; i
< max(popcount
, number
); i
++) {
504 * A few caps (such as plab_norm) have string-valued parms.
505 * We'll have to assume that the caller knows the difference, since
506 * a char* and an int may not be the same size on the stack.
508 if (p_is_s
[i
] != 0) {
509 p_is_s
[i
] = va_arg(ap
, char *);
511 param
[i
] = va_arg(ap
, long int);
516 * This is a termcap compatibility hack. If there are no explicit pop
517 * operations in the string, load the stack in such a way that
518 * successive pops will grab successive parameters. That will make
519 * the expansion of (for example) \E[%d;%dH work correctly in termcap
520 * style, which means tparam() will expand termcap strings OK.
525 for (i
= number
- 1; i
>= 0; i
--)
529 while ((cp
- string
) < (int) len2
) {
531 save_char(UChar(*cp
));
534 cp
= parse_format(cp
, fmt_buff
, &len
);
542 case 'd': /* FALLTHRU */
543 case 'o': /* FALLTHRU */
544 case 'x': /* FALLTHRU */
545 case 'X': /* FALLTHRU */
546 save_number(fmt_buff
, npop(), len
);
549 case 'c': /* FALLTHRU */
554 save_number("%d", (int) grub_strlen(spop()), 0);
558 save_text(fmt_buff
, spop(), len
);
563 i
= (UChar(*cp
) - '1');
564 if (i
>= 0 && i
< NUM_PARM
) {
575 i
= (UChar(*cp
) - 'A');
576 static_vars
[i
] = npop();
577 } else if (isLOWER(*cp
)) {
578 i
= (UChar(*cp
) - 'a');
579 dynamic_var
[i
] = npop();
586 i
= (UChar(*cp
) - 'A');
587 npush(static_vars
[i
]);
588 } else if (isLOWER(*cp
)) {
589 i
= (UChar(*cp
) - 'a');
590 npush(dynamic_var
[i
]);
603 while (isdigit(UChar(*cp
))) {
604 number
= (number
* 10) + (UChar(*cp
) - '0');
611 npush(npop() + npop());
621 npush(npop() * npop());
627 npush(y
? (x
/ y
) : 0);
633 npush(y
? (x
% y
) : 0);
637 npush(npop() && npop());
641 npush(npop() || npop());
645 npush(npop() & npop());
649 npush(npop() | npop());
653 npush(npop() ^ npop());
695 /* scan forward for %e or %; at level zero */
703 else if (*cp
== ';') {
708 } else if (*cp
== 'e' && level
== 0)
719 /* scan forward for a %; at level zero */
727 else if (*cp
== ';') {
743 } /* endswitch (*cp) */
744 } /* endelse (*cp == '%') */
750 } /* endwhile (*cp) */
753 out_buff
[out_used
] = '\0';
759 grub_terminfo_tparm (const char *string
, ...)
764 va_start (ap
, string
);
765 result
= tparam_internal (string
, ap
);