2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1998-2003,2004,2005 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 /**********************************************************************
20 * This code is a modification of lib_tparm.c found in ncurses-5.2. The
21 * modification are for use in grub by replacing all libc function through
22 * special grub functions. This also meant to delete all dynamic memory
23 * allocation and replace it by a number of fixed buffers.
25 * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002
27 * Resync with ncurses-5.4 by Omniflux <omniflux+devel@omniflux.com> 2005
28 **********************************************************************/
30 /****************************************************************************
31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
32 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
33 * and: Thomas E. Dickey, 1996 on *
34 ****************************************************************************/
41 #include <grub/misc.h>
43 #include <grub/types.h>
44 #include <grub/tparm.h>
47 * Common/troublesome character definitions
49 typedef char grub_bool_t
;
54 # define TRUE (!FALSE)
60 #define MAX_FORMAT_LEN 256
62 #define max(a,b) ((a) > (b) ? (a) : (b))
63 #define isdigit(c) ((c) >= '0' && (c) <= '9')
64 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
65 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
67 #define UChar(c) ((unsigned char)(c))
75 * Substitute the given parameters into the given string by the following
76 * rules (taken from terminfo(5)):
78 * Cursor addressing and other strings requiring parame-
79 * ters in the terminal are described by a parameterized string
80 * capability, with like escapes %x in it. For example, to
81 * address the cursor, the cup capability is given, using two
82 * parameters: the row and column to address to. (Rows and
83 * columns are numbered from zero and refer to the physical
84 * screen visible to the user, not to any unseen memory.) If
85 * the terminal has memory relative cursor addressing, that can
88 * The parameter mechanism uses a stack and special %
89 * codes to manipulate it. Typically a sequence will push one
90 * of the parameters onto the stack and then print it in some
91 * format. Often more complex operations are necessary.
93 * The % encodings have the following meanings:
96 * %c print pop() like %c in printf()
97 * %s print pop() like %s in printf()
98 * %[[:]flags][width[.precision]][doxXs]
99 * as in printf, flags are [-+#] and space
100 * The ':' is used to avoid making %+ or %-
101 * patterns (see below).
103 * %p[1-9] push ith parm
104 * %P[a-z] set dynamic variable [a-z] to pop()
105 * %g[a-z] get dynamic variable [a-z] and push it
106 * %P[A-Z] set static variable [A-Z] to pop()
107 * %g[A-Z] get static variable [A-Z] and push it
108 * %l push strlen(pop)
109 * %'c' push char constant c
110 * %{nn} push integer constant nn
113 * arithmetic (%m is mod): push(pop() op pop())
114 * %& %| %^ bit operations: push(pop() op pop())
115 * %= %> %< logical operations: push(pop() op pop())
116 * %A %O logical and & or operations for conditionals
117 * %! %~ unary operations push(op pop())
118 * %i add 1 to first two parms (for ANSI terminals)
120 * %? expr %t thenpart %e elsepart %;
121 * if-then-else, %e elsepart is optional.
122 * else-if's are possible ala Algol 68:
123 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
125 * For those of the above operators which are binary and not commutative,
126 * the stack works in the usual way, with
128 * resulting in x mod y, not the reverse.
136 grub_bool_t num_type
;
139 static stack_frame stack
[STACKSIZE
];
140 static int stack_ptr
;
141 static const char *tparam_base
= "";
143 static char *out_buff
;
144 static grub_size_t out_size
;
145 static grub_size_t out_used
;
147 static char *fmt_buff
;
148 static grub_size_t fmt_size
;
151 get_space(grub_size_t need
)
154 if (need
> out_size
) {
156 out_buff
= grub_realloc(out_buff
, out_size
*sizeof(char));
157 /* FIX ME! handle out_buff == 0. */
162 save_text(const char *fmt
, const char *s
, int len
)
164 grub_size_t s_len
= grub_strlen(s
);
165 if (len
> (int) s_len
)
168 get_space(s_len
+ 1);
170 (void) grub_snprintf(out_buff
+ out_used
, s_len
+ 1, fmt
, s
);
171 out_used
+= grub_strlen(out_buff
+ out_used
);
175 save_number(const char *fmt
, int number
, int len
)
178 len
= 30; /* actually log10(MAX_INT)+1 */
180 get_space((unsigned) len
+ 1);
182 (void) grub_snprintf(out_buff
+ out_used
, len
+ 1, fmt
, number
);
183 out_used
+= grub_strlen(out_buff
+ out_used
);
192 out_buff
[out_used
++] = c
;
198 if (stack_ptr
< STACKSIZE
) {
199 stack
[stack_ptr
].num_type
= TRUE
;
200 stack
[stack_ptr
].data
.num
= x
;
211 if (stack
[stack_ptr
].num_type
)
212 result
= stack
[stack_ptr
].data
.num
;
220 if (stack_ptr
< STACKSIZE
) {
221 stack
[stack_ptr
].num_type
= FALSE
;
222 stack
[stack_ptr
].data
.str
= x
;
230 static char dummy
[] = ""; /* avoid const-cast */
231 char *result
= dummy
;
234 if (!stack
[stack_ptr
].num_type
&& stack
[stack_ptr
].data
.str
!= 0)
235 result
= stack
[stack_ptr
].data
.str
;
240 static inline const char *
241 parse_format(const char *s
, char *format
, int *len
)
245 grub_bool_t done
= FALSE
;
246 grub_bool_t allowminus
= FALSE
;
247 grub_bool_t dot
= FALSE
;
248 grub_bool_t err
= FALSE
;
256 while (*s
!= '\0' && !done
) {
258 case 'c': /* FALLTHRU */
259 case 'd': /* FALLTHRU */
260 case 'o': /* FALLTHRU */
261 case 'x': /* FALLTHRU */
262 case 'X': /* FALLTHRU */
271 } else { /* value before '.' is the width */
295 if (isdigit(UChar(*s
))) {
296 value
= (value
* 10) + (*s
- '0');
307 * If we found an error, ignore (and remove) the flags.
310 my_width
= my_prec
= value
= 0;
317 * Any value after '.' is the precision. If we did not see '.', then
318 * the value is the width.
326 /* return maximum string length in print */
327 *len
= (my_width
> my_prec
) ? my_width
: my_prec
;
333 * Analyze the string to see how many parameters we need from the varargs list,
334 * and what their types are. We will only accept string parameters if they
335 * appear as a %l or %s format following an explicit parameter reference (e.g.,
336 * %p2%s). All other parameters are numbers.
338 * 'number' counts coarsely the number of pop's we see in the string, and
339 * 'popcount' shows the highest parameter number in the string. We would like
340 * to simply use the latter count, but if we are reading termcap strings, there
341 * may be cases that we cannot see the explicit parameter numbers.
344 analyze(const char *string
, char *p_is_s
[NUM_PARM
], int *popcount
)
351 const char *cp
= string
;
352 static char dummy
[] = "";
359 if ((len2
= grub_strlen(cp
)) > fmt_size
) {
360 fmt_size
= len2
+ fmt_size
+ 2;
361 if ((fmt_buff
= grub_realloc(fmt_buff
, fmt_size
*sizeof(char))) == 0)
365 grub_memset(p_is_s
, 0, sizeof(p_is_s
[0]) * NUM_PARM
);
367 while ((cp
- string
) < (int) len2
) {
370 cp
= parse_format(cp
, fmt_buff
, &len
);
375 case 'd': /* FALLTHRU */
376 case 'o': /* FALLTHRU */
377 case 'x': /* FALLTHRU */
378 case 'X': /* FALLTHRU */
379 case 'c': /* FALLTHRU */
388 p_is_s
[lastpop
- 1] = dummy
;
394 i
= (UChar(*cp
) - '0');
395 if (i
>= 0 && i
<= NUM_PARM
) {
397 if (lastpop
> *popcount
)
418 while (isdigit(UChar(*cp
))) {
447 /* will add 1 to first (usually two) parameters */
455 if (number
> NUM_PARM
)
461 tparam_internal(const char *string
, va_list ap
)
463 char *p_is_s
[NUM_PARM
];
464 long param
[NUM_PARM
];
471 const char *cp
= string
;
473 static int dynamic_var
[NUM_VARS
];
474 static int static_vars
[NUM_VARS
];
479 out_used
= out_size
= fmt_size
= 0;
481 len2
= (int) grub_strlen(cp
);
484 * Find the highest parameter-number referred to in the format string.
485 * Use this value to limit the number of arguments copied from the
486 * variable-length argument list.
488 number
= analyze(cp
, p_is_s
, &popcount
);
492 for (i
= 0; i
< max(popcount
, number
); i
++) {
494 * A few caps (such as plab_norm) have string-valued parms.
495 * We'll have to assume that the caller knows the difference, since
496 * a char* and an int may not be the same size on the stack.
498 if (p_is_s
[i
] != 0) {
499 p_is_s
[i
] = va_arg(ap
, char *);
501 param
[i
] = va_arg(ap
, long int);
506 * This is a termcap compatibility hack. If there are no explicit pop
507 * operations in the string, load the stack in such a way that
508 * successive pops will grab successive parameters. That will make
509 * the expansion of (for example) \E[%d;%dH work correctly in termcap
510 * style, which means tparam() will expand termcap strings OK.
515 for (i
= number
- 1; i
>= 0; i
--)
519 while ((cp
- string
) < (int) len2
) {
521 save_char(UChar(*cp
));
524 cp
= parse_format(cp
, fmt_buff
, &len
);
532 case 'd': /* FALLTHRU */
533 case 'o': /* FALLTHRU */
534 case 'x': /* FALLTHRU */
535 case 'X': /* FALLTHRU */
536 save_number(fmt_buff
, npop(), len
);
539 case 'c': /* FALLTHRU */
544 save_number("%d", (int) grub_strlen(spop()), 0);
548 save_text(fmt_buff
, spop(), len
);
553 i
= (UChar(*cp
) - '1');
554 if (i
>= 0 && i
< NUM_PARM
) {
565 i
= (UChar(*cp
) - 'A');
566 static_vars
[i
] = npop();
567 } else if (isLOWER(*cp
)) {
568 i
= (UChar(*cp
) - 'a');
569 dynamic_var
[i
] = npop();
576 i
= (UChar(*cp
) - 'A');
577 npush(static_vars
[i
]);
578 } else if (isLOWER(*cp
)) {
579 i
= (UChar(*cp
) - 'a');
580 npush(dynamic_var
[i
]);
593 while (isdigit(UChar(*cp
))) {
594 number
= (number
* 10) + (UChar(*cp
) - '0');
601 npush(npop() + npop());
611 npush(npop() * npop());
617 npush(y
? (x
/ y
) : 0);
623 npush(y
? (x
% y
) : 0);
627 npush(npop() && npop());
631 npush(npop() || npop());
635 npush(npop() & npop());
639 npush(npop() | npop());
643 npush(npop() ^ npop());
685 /* scan forward for %e or %; at level zero */
693 else if (*cp
== ';') {
698 } else if (*cp
== 'e' && level
== 0)
709 /* scan forward for a %; at level zero */
717 else if (*cp
== ';') {
733 } /* endswitch (*cp) */
734 } /* endelse (*cp == '%') */
740 } /* endwhile (*cp) */
743 out_buff
[out_used
] = '\0';
749 grub_terminfo_tparm (const char *string
, ...)
754 va_start (ap
, string
);
755 result
= tparam_internal (string
, ap
);