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. */
161 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
164 save_text(const char *fmt
, const char *s
, int len
)
166 grub_size_t s_len
= grub_strlen(s
);
167 if (len
> (int) s_len
)
170 get_space(s_len
+ 1);
172 (void) grub_snprintf(out_buff
+ out_used
, s_len
+ 1, fmt
, s
);
173 out_used
+= grub_strlen(out_buff
+ out_used
);
177 save_number(const char *fmt
, int number
, int len
)
180 len
= 30; /* actually log10(MAX_INT)+1 */
182 get_space((unsigned) len
+ 1);
184 (void) grub_snprintf(out_buff
+ out_used
, len
+ 1, fmt
, number
);
185 out_used
+= grub_strlen(out_buff
+ out_used
);
188 #pragma GCC diagnostic error "-Wformat-nonliteral"
196 out_buff
[out_used
++] = c
;
202 if (stack_ptr
< STACKSIZE
) {
203 stack
[stack_ptr
].num_type
= TRUE
;
204 stack
[stack_ptr
].data
.num
= x
;
215 if (stack
[stack_ptr
].num_type
)
216 result
= stack
[stack_ptr
].data
.num
;
224 if (stack_ptr
< STACKSIZE
) {
225 stack
[stack_ptr
].num_type
= FALSE
;
226 stack
[stack_ptr
].data
.str
= x
;
234 static char dummy
[] = ""; /* avoid const-cast */
235 char *result
= dummy
;
238 if (!stack
[stack_ptr
].num_type
&& stack
[stack_ptr
].data
.str
!= 0)
239 result
= stack
[stack_ptr
].data
.str
;
244 static inline const char *
245 parse_format(const char *s
, char *format
, int *len
)
249 grub_bool_t done
= FALSE
;
250 grub_bool_t allowminus
= FALSE
;
251 grub_bool_t dot
= FALSE
;
252 grub_bool_t err
= FALSE
;
260 while (*s
!= '\0' && !done
) {
262 case 'c': /* FALLTHRU */
263 case 'd': /* FALLTHRU */
264 case 'o': /* FALLTHRU */
265 case 'x': /* FALLTHRU */
266 case 'X': /* FALLTHRU */
275 } else { /* value before '.' is the width */
299 if (isdigit(UChar(*s
))) {
300 value
= (value
* 10) + (*s
- '0');
311 * If we found an error, ignore (and remove) the flags.
314 my_width
= my_prec
= value
= 0;
321 * Any value after '.' is the precision. If we did not see '.', then
322 * the value is the width.
330 /* return maximum string length in print */
331 *len
= (my_width
> my_prec
) ? my_width
: my_prec
;
337 * Analyze the string to see how many parameters we need from the varargs list,
338 * and what their types are. We will only accept string parameters if they
339 * appear as a %l or %s format following an explicit parameter reference (e.g.,
340 * %p2%s). All other parameters are numbers.
342 * 'number' counts coarsely the number of pop's we see in the string, and
343 * 'popcount' shows the highest parameter number in the string. We would like
344 * to simply use the latter count, but if we are reading termcap strings, there
345 * may be cases that we cannot see the explicit parameter numbers.
348 analyze(const char *string
, char *p_is_s
[NUM_PARM
], int *popcount
)
355 const char *cp
= string
;
356 static char dummy
[] = "";
363 if ((len2
= grub_strlen(cp
)) > fmt_size
) {
364 fmt_size
= len2
+ fmt_size
+ 2;
365 if ((fmt_buff
= grub_realloc(fmt_buff
, fmt_size
*sizeof(char))) == 0)
369 grub_memset(p_is_s
, 0, sizeof(p_is_s
[0]) * NUM_PARM
);
371 while ((cp
- string
) < (int) len2
) {
374 cp
= parse_format(cp
, fmt_buff
, &len
);
379 case 'd': /* FALLTHRU */
380 case 'o': /* FALLTHRU */
381 case 'x': /* FALLTHRU */
382 case 'X': /* FALLTHRU */
383 case 'c': /* FALLTHRU */
392 p_is_s
[lastpop
- 1] = dummy
;
398 i
= (UChar(*cp
) - '0');
399 if (i
>= 0 && i
<= NUM_PARM
) {
401 if (lastpop
> *popcount
)
422 while (isdigit(UChar(*cp
))) {
451 /* will add 1 to first (usually two) parameters */
459 if (number
> NUM_PARM
)
465 tparam_internal(const char *string
, va_list ap
)
467 char *p_is_s
[NUM_PARM
];
468 long param
[NUM_PARM
];
475 const char *cp
= string
;
477 static int dynamic_var
[NUM_VARS
];
478 static int static_vars
[NUM_VARS
];
483 out_used
= out_size
= fmt_size
= 0;
485 len2
= (int) grub_strlen(cp
);
488 * Find the highest parameter-number referred to in the format string.
489 * Use this value to limit the number of arguments copied from the
490 * variable-length argument list.
492 number
= analyze(cp
, p_is_s
, &popcount
);
496 for (i
= 0; i
< max(popcount
, number
); i
++) {
498 * A few caps (such as plab_norm) have string-valued parms.
499 * We'll have to assume that the caller knows the difference, since
500 * a char* and an int may not be the same size on the stack.
502 if (p_is_s
[i
] != 0) {
503 p_is_s
[i
] = va_arg(ap
, char *);
505 param
[i
] = va_arg(ap
, long int);
510 * This is a termcap compatibility hack. If there are no explicit pop
511 * operations in the string, load the stack in such a way that
512 * successive pops will grab successive parameters. That will make
513 * the expansion of (for example) \E[%d;%dH work correctly in termcap
514 * style, which means tparam() will expand termcap strings OK.
519 for (i
= number
- 1; i
>= 0; i
--)
523 while ((cp
- string
) < (int) len2
) {
525 save_char(UChar(*cp
));
528 cp
= parse_format(cp
, fmt_buff
, &len
);
536 case 'd': /* FALLTHRU */
537 case 'o': /* FALLTHRU */
538 case 'x': /* FALLTHRU */
539 case 'X': /* FALLTHRU */
540 save_number(fmt_buff
, npop(), len
);
543 case 'c': /* FALLTHRU */
548 save_number("%d", (int) grub_strlen(spop()), 0);
552 save_text(fmt_buff
, spop(), len
);
557 i
= (UChar(*cp
) - '1');
558 if (i
>= 0 && i
< NUM_PARM
) {
569 i
= (UChar(*cp
) - 'A');
570 static_vars
[i
] = npop();
571 } else if (isLOWER(*cp
)) {
572 i
= (UChar(*cp
) - 'a');
573 dynamic_var
[i
] = npop();
580 i
= (UChar(*cp
) - 'A');
581 npush(static_vars
[i
]);
582 } else if (isLOWER(*cp
)) {
583 i
= (UChar(*cp
) - 'a');
584 npush(dynamic_var
[i
]);
597 while (isdigit(UChar(*cp
))) {
598 number
= (number
* 10) + (UChar(*cp
) - '0');
605 npush(npop() + npop());
615 npush(npop() * npop());
621 /* GRUB has no signed divisions. */
622 npush(y
? ((unsigned)x
/ (unsigned)y
) : 0);
628 /* GRUB has no signed divisions. */
629 npush(y
? ((unsigned)x
% (unsigned)y
) : 0);
633 npush(npop() && npop());
637 npush(npop() || npop());
641 npush(npop() & npop());
645 npush(npop() | npop());
649 npush(npop() ^ npop());
691 /* scan forward for %e or %; at level zero */
699 else if (*cp
== ';') {
704 } else if (*cp
== 'e' && level
== 0)
715 /* scan forward for a %; at level zero */
723 else if (*cp
== ';') {
739 } /* endswitch (*cp) */
740 } /* endelse (*cp == '%') */
746 } /* endwhile (*cp) */
749 out_buff
[out_used
] = '\0';
755 grub_terminfo_tparm (const char *string
, ...)
763 va_start (ap
, string
);
764 result
= tparam_internal (string
, ap
);