Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / term / tparm.c
blob8c1c288a0bb1c579fe61ecedf2abd43c39a4f321
1 /*
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 ****************************************************************************/
37 * tparm.c
41 #include <grub/misc.h>
42 #include <grub/mm.h>
43 #include <grub/types.h>
44 #include <grub/tparm.h>
47 * Common/troublesome character definitions
49 typedef char grub_bool_t;
50 #ifndef FALSE
51 # define FALSE (0)
52 #endif
53 #ifndef TRUE
54 # define TRUE (!FALSE)
55 #endif
57 #define NUM_PARM 9
58 #define NUM_VARS 26
59 #define STACKSIZE 20
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))
69 //MODULE_ID("$Id$")
72 * char *
73 * tparm(string, ...)
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
86 * be indicated by
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:
95 * %% outputs `%'
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
112 * %+ %- %* %/ %m
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
127 * %gx %gy %m
128 * resulting in x mod y, not the reverse.
131 typedef struct {
132 union {
133 int num;
134 char *str;
135 } data;
136 grub_bool_t num_type;
137 } stack_frame;
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;
150 static inline void
151 get_space(grub_size_t need)
153 need += out_used;
154 if (need > out_size) {
155 out_size = need * 2;
156 out_buff = grub_realloc(out_buff, out_size*sizeof(char));
157 /* FIX ME! handle out_buff == 0. */
161 static inline void
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)
166 s_len = 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);
174 static inline void
175 save_number(const char *fmt, int number, int len)
177 if (len < 30)
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);
186 static inline void
187 save_char(int c)
189 if (c == 0)
190 c = 0200;
191 get_space(1);
192 out_buff[out_used++] = c;
195 static inline void
196 npush(int x)
198 if (stack_ptr < STACKSIZE) {
199 stack[stack_ptr].num_type = TRUE;
200 stack[stack_ptr].data.num = x;
201 stack_ptr++;
205 static inline int
206 npop(void)
208 int result = 0;
209 if (stack_ptr > 0) {
210 stack_ptr--;
211 if (stack[stack_ptr].num_type)
212 result = stack[stack_ptr].data.num;
214 return result;
217 static inline void
218 spush(char *x)
220 if (stack_ptr < STACKSIZE) {
221 stack[stack_ptr].num_type = FALSE;
222 stack[stack_ptr].data.str = x;
223 stack_ptr++;
227 static inline char *
228 spop(void)
230 static char dummy[] = ""; /* avoid const-cast */
231 char *result = dummy;
232 if (stack_ptr > 0) {
233 stack_ptr--;
234 if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
235 result = stack[stack_ptr].data.str;
237 return result;
240 static inline const char *
241 parse_format(const char *s, char *format, int *len)
243 *len = 0;
244 if (format != 0) {
245 grub_bool_t done = FALSE;
246 grub_bool_t allowminus = FALSE;
247 grub_bool_t dot = FALSE;
248 grub_bool_t err = FALSE;
249 char *fmt = format;
250 int my_width = 0;
251 int my_prec = 0;
252 int value = 0;
254 *len = 0;
255 *format++ = '%';
256 while (*s != '\0' && !done) {
257 switch (*s) {
258 case 'c': /* FALLTHRU */
259 case 'd': /* FALLTHRU */
260 case 'o': /* FALLTHRU */
261 case 'x': /* FALLTHRU */
262 case 'X': /* FALLTHRU */
263 case 's':
264 *format++ = *s;
265 done = TRUE;
266 break;
267 case '.':
268 *format++ = *s++;
269 if (dot) {
270 err = TRUE;
271 } else { /* value before '.' is the width */
272 dot = TRUE;
273 my_width = value;
275 value = 0;
276 break;
277 case '#':
278 *format++ = *s++;
279 break;
280 case ' ':
281 *format++ = *s++;
282 break;
283 case ':':
284 s++;
285 allowminus = TRUE;
286 break;
287 case '-':
288 if (allowminus) {
289 *format++ = *s++;
290 } else {
291 done = TRUE;
293 break;
294 default:
295 if (isdigit(UChar(*s))) {
296 value = (value * 10) + (*s - '0');
297 if (value > 10000)
298 err = TRUE;
299 *format++ = *s++;
300 } else {
301 done = TRUE;
307 * If we found an error, ignore (and remove) the flags.
309 if (err) {
310 my_width = my_prec = value = 0;
311 format = fmt;
312 *format++ = '%';
313 *format++ = *s;
317 * Any value after '.' is the precision. If we did not see '.', then
318 * the value is the width.
320 if (dot)
321 my_prec = value;
322 else
323 my_width = value;
325 *format = '\0';
326 /* return maximum string length in print */
327 *len = (my_width > my_prec) ? my_width : my_prec;
329 return s;
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.
343 static inline int
344 analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
346 grub_size_t len2;
347 int i;
348 int lastpop = -1;
349 int len;
350 int number = 0;
351 const char *cp = string;
352 static char dummy[] = "";
354 *popcount = 0;
356 if (cp == 0)
357 return 0;
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)
362 return 0;
365 grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
367 while ((cp - string) < (int) len2) {
368 if (*cp == '%') {
369 cp++;
370 cp = parse_format(cp, fmt_buff, &len);
371 switch (*cp) {
372 default:
373 break;
375 case 'd': /* FALLTHRU */
376 case 'o': /* FALLTHRU */
377 case 'x': /* FALLTHRU */
378 case 'X': /* FALLTHRU */
379 case 'c': /* FALLTHRU */
380 if (lastpop <= 0)
381 number++;
382 lastpop = -1;
383 break;
385 case 'l':
386 case 's':
387 if (lastpop > 0)
388 p_is_s[lastpop - 1] = dummy;
389 ++number;
390 break;
392 case 'p':
393 cp++;
394 i = (UChar(*cp) - '0');
395 if (i >= 0 && i <= NUM_PARM) {
396 lastpop = i;
397 if (lastpop > *popcount)
398 *popcount = lastpop;
400 break;
402 case 'P':
403 ++number;
404 ++cp;
405 break;
407 case 'g':
408 cp++;
409 break;
411 case '\'':
412 cp += 2;
413 lastpop = -1;
414 break;
416 case '{':
417 cp++;
418 while (isdigit(UChar(*cp))) {
419 cp++;
421 break;
423 case '+':
424 case '-':
425 case '*':
426 case '/':
427 case 'm':
428 case 'A':
429 case 'O':
430 case '&':
431 case '|':
432 case '^':
433 case '=':
434 case '<':
435 case '>':
436 lastpop = -1;
437 number += 2;
438 break;
440 case '!':
441 case '~':
442 lastpop = -1;
443 ++number;
444 break;
446 case 'i':
447 /* will add 1 to first (usually two) parameters */
448 break;
451 if (*cp != '\0')
452 cp++;
455 if (number > NUM_PARM)
456 number = NUM_PARM;
457 return number;
460 static inline char *
461 tparam_internal(const char *string, va_list ap)
463 char *p_is_s[NUM_PARM];
464 long param[NUM_PARM];
465 int popcount;
466 int number;
467 int len;
468 int level;
469 int x, y;
470 int i;
471 const char *cp = string;
472 grub_size_t len2;
473 static int dynamic_var[NUM_VARS];
474 static int static_vars[NUM_VARS];
476 if (cp == 0)
477 return 0;
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);
489 if (fmt_buff == 0)
490 return 0;
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 *);
500 } else {
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.
512 stack_ptr = 0;
513 if (popcount == 0) {
514 popcount = number;
515 for (i = number - 1; i >= 0; i--)
516 npush(param[i]);
519 while ((cp - string) < (int) len2) {
520 if (*cp != '%') {
521 save_char(UChar(*cp));
522 } else {
523 tparam_base = cp++;
524 cp = parse_format(cp, fmt_buff, &len);
525 switch (*cp) {
526 default:
527 break;
528 case '%':
529 save_char('%');
530 break;
532 case 'd': /* FALLTHRU */
533 case 'o': /* FALLTHRU */
534 case 'x': /* FALLTHRU */
535 case 'X': /* FALLTHRU */
536 save_number(fmt_buff, npop(), len);
537 break;
539 case 'c': /* FALLTHRU */
540 save_char(npop());
541 break;
543 case 'l':
544 save_number("%d", (int) grub_strlen(spop()), 0);
545 break;
547 case 's':
548 save_text(fmt_buff, spop(), len);
549 break;
551 case 'p':
552 cp++;
553 i = (UChar(*cp) - '1');
554 if (i >= 0 && i < NUM_PARM) {
555 if (p_is_s[i])
556 spush(p_is_s[i]);
557 else
558 npush(param[i]);
560 break;
562 case 'P':
563 cp++;
564 if (isUPPER(*cp)) {
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();
571 break;
573 case 'g':
574 cp++;
575 if (isUPPER(*cp)) {
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]);
582 break;
584 case '\'':
585 cp++;
586 npush(UChar(*cp));
587 cp++;
588 break;
590 case '{':
591 number = 0;
592 cp++;
593 while (isdigit(UChar(*cp))) {
594 number = (number * 10) + (UChar(*cp) - '0');
595 cp++;
597 npush(number);
598 break;
600 case '+':
601 npush(npop() + npop());
602 break;
604 case '-':
605 y = npop();
606 x = npop();
607 npush(x - y);
608 break;
610 case '*':
611 npush(npop() * npop());
612 break;
614 case '/':
615 y = npop();
616 x = npop();
617 npush(y ? (x / y) : 0);
618 break;
620 case 'm':
621 y = npop();
622 x = npop();
623 npush(y ? (x % y) : 0);
624 break;
626 case 'A':
627 npush(npop() && npop());
628 break;
630 case 'O':
631 npush(npop() || npop());
632 break;
634 case '&':
635 npush(npop() & npop());
636 break;
638 case '|':
639 npush(npop() | npop());
640 break;
642 case '^':
643 npush(npop() ^ npop());
644 break;
646 case '=':
647 y = npop();
648 x = npop();
649 npush(x == y);
650 break;
652 case '<':
653 y = npop();
654 x = npop();
655 npush(x < y);
656 break;
658 case '>':
659 y = npop();
660 x = npop();
661 npush(x > y);
662 break;
664 case '!':
665 npush(!npop());
666 break;
668 case '~':
669 npush(~npop());
670 break;
672 case 'i':
673 if (p_is_s[0] == 0)
674 param[0]++;
675 if (p_is_s[1] == 0)
676 param[1]++;
677 break;
679 case '?':
680 break;
682 case 't':
683 x = npop();
684 if (!x) {
685 /* scan forward for %e or %; at level zero */
686 cp++;
687 level = 0;
688 while (*cp) {
689 if (*cp == '%') {
690 cp++;
691 if (*cp == '?')
692 level++;
693 else if (*cp == ';') {
694 if (level > 0)
695 level--;
696 else
697 break;
698 } else if (*cp == 'e' && level == 0)
699 break;
702 if (*cp)
703 cp++;
706 break;
708 case 'e':
709 /* scan forward for a %; at level zero */
710 cp++;
711 level = 0;
712 while (*cp) {
713 if (*cp == '%') {
714 cp++;
715 if (*cp == '?')
716 level++;
717 else if (*cp == ';') {
718 if (level > 0)
719 level--;
720 else
721 break;
725 if (*cp)
726 cp++;
728 break;
730 case ';':
731 break;
733 } /* endswitch (*cp) */
734 } /* endelse (*cp == '%') */
736 if (*cp == '\0')
737 break;
739 cp++;
740 } /* endwhile (*cp) */
742 get_space(1);
743 out_buff[out_used] = '\0';
745 return (out_buff);
748 const char *
749 grub_terminfo_tparm (const char *string, ...)
751 va_list ap;
752 char *result;
754 if (!string)
755 return "";
757 va_start (ap, string);
758 result = tparam_internal (string, ap);
759 va_end (ap);
760 return result;