Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / term / tparm.c
blobfb5b15a88d731cc8a191ba9aeb4010d0868bb474
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 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
163 static inline void
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)
168 s_len = 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);
176 static inline void
177 save_number(const char *fmt, int number, int len)
179 if (len < 30)
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"
190 static inline void
191 save_char(int c)
193 if (c == 0)
194 c = 0200;
195 get_space(1);
196 out_buff[out_used++] = c;
199 static inline void
200 npush(int x)
202 if (stack_ptr < STACKSIZE) {
203 stack[stack_ptr].num_type = TRUE;
204 stack[stack_ptr].data.num = x;
205 stack_ptr++;
209 static inline int
210 npop(void)
212 int result = 0;
213 if (stack_ptr > 0) {
214 stack_ptr--;
215 if (stack[stack_ptr].num_type)
216 result = stack[stack_ptr].data.num;
218 return result;
221 static inline void
222 spush(char *x)
224 if (stack_ptr < STACKSIZE) {
225 stack[stack_ptr].num_type = FALSE;
226 stack[stack_ptr].data.str = x;
227 stack_ptr++;
231 static inline char *
232 spop(void)
234 static char dummy[] = ""; /* avoid const-cast */
235 char *result = dummy;
236 if (stack_ptr > 0) {
237 stack_ptr--;
238 if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
239 result = stack[stack_ptr].data.str;
241 return result;
244 static inline const char *
245 parse_format(const char *s, char *format, int *len)
247 *len = 0;
248 if (format != 0) {
249 grub_bool_t done = FALSE;
250 grub_bool_t allowminus = FALSE;
251 grub_bool_t dot = FALSE;
252 grub_bool_t err = FALSE;
253 char *fmt = format;
254 int my_width = 0;
255 int my_prec = 0;
256 int value = 0;
258 *len = 0;
259 *format++ = '%';
260 while (*s != '\0' && !done) {
261 switch (*s) {
262 case 'c': /* FALLTHRU */
263 case 'd': /* FALLTHRU */
264 case 'o': /* FALLTHRU */
265 case 'x': /* FALLTHRU */
266 case 'X': /* FALLTHRU */
267 case 's':
268 *format++ = *s;
269 done = TRUE;
270 break;
271 case '.':
272 *format++ = *s++;
273 if (dot) {
274 err = TRUE;
275 } else { /* value before '.' is the width */
276 dot = TRUE;
277 my_width = value;
279 value = 0;
280 break;
281 case '#':
282 *format++ = *s++;
283 break;
284 case ' ':
285 *format++ = *s++;
286 break;
287 case ':':
288 s++;
289 allowminus = TRUE;
290 break;
291 case '-':
292 if (allowminus) {
293 *format++ = *s++;
294 } else {
295 done = TRUE;
297 break;
298 default:
299 if (isdigit(UChar(*s))) {
300 value = (value * 10) + (*s - '0');
301 if (value > 10000)
302 err = TRUE;
303 *format++ = *s++;
304 } else {
305 done = TRUE;
311 * If we found an error, ignore (and remove) the flags.
313 if (err) {
314 my_width = my_prec = value = 0;
315 format = fmt;
316 *format++ = '%';
317 *format++ = *s;
321 * Any value after '.' is the precision. If we did not see '.', then
322 * the value is the width.
324 if (dot)
325 my_prec = value;
326 else
327 my_width = value;
329 *format = '\0';
330 /* return maximum string length in print */
331 *len = (my_width > my_prec) ? my_width : my_prec;
333 return s;
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.
347 static inline int
348 analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
350 grub_size_t len2;
351 int i;
352 int lastpop = -1;
353 int len;
354 int number = 0;
355 const char *cp = string;
356 static char dummy[] = "";
358 *popcount = 0;
360 if (cp == 0)
361 return 0;
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)
366 return 0;
369 grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
371 while ((cp - string) < (int) len2) {
372 if (*cp == '%') {
373 cp++;
374 cp = parse_format(cp, fmt_buff, &len);
375 switch (*cp) {
376 default:
377 break;
379 case 'd': /* FALLTHRU */
380 case 'o': /* FALLTHRU */
381 case 'x': /* FALLTHRU */
382 case 'X': /* FALLTHRU */
383 case 'c': /* FALLTHRU */
384 if (lastpop <= 0)
385 number++;
386 lastpop = -1;
387 break;
389 case 'l':
390 case 's':
391 if (lastpop > 0)
392 p_is_s[lastpop - 1] = dummy;
393 ++number;
394 break;
396 case 'p':
397 cp++;
398 i = (UChar(*cp) - '0');
399 if (i >= 0 && i <= NUM_PARM) {
400 lastpop = i;
401 if (lastpop > *popcount)
402 *popcount = lastpop;
404 break;
406 case 'P':
407 ++number;
408 ++cp;
409 break;
411 case 'g':
412 cp++;
413 break;
415 case '\'':
416 cp += 2;
417 lastpop = -1;
418 break;
420 case '{':
421 cp++;
422 while (isdigit(UChar(*cp))) {
423 cp++;
425 break;
427 case '+':
428 case '-':
429 case '*':
430 case '/':
431 case 'm':
432 case 'A':
433 case 'O':
434 case '&':
435 case '|':
436 case '^':
437 case '=':
438 case '<':
439 case '>':
440 lastpop = -1;
441 number += 2;
442 break;
444 case '!':
445 case '~':
446 lastpop = -1;
447 ++number;
448 break;
450 case 'i':
451 /* will add 1 to first (usually two) parameters */
452 break;
455 if (*cp != '\0')
456 cp++;
459 if (number > NUM_PARM)
460 number = NUM_PARM;
461 return number;
464 static inline char *
465 tparam_internal(const char *string, va_list ap)
467 char *p_is_s[NUM_PARM];
468 long param[NUM_PARM];
469 int popcount;
470 int number;
471 int len;
472 int level;
473 int x, y;
474 int i;
475 const char *cp = string;
476 grub_size_t len2;
477 static int dynamic_var[NUM_VARS];
478 static int static_vars[NUM_VARS];
480 if (cp == 0)
481 return 0;
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);
493 if (fmt_buff == 0)
494 return 0;
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 *);
504 } else {
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.
516 stack_ptr = 0;
517 if (popcount == 0) {
518 popcount = number;
519 for (i = number - 1; i >= 0; i--)
520 npush(param[i]);
523 while ((cp - string) < (int) len2) {
524 if (*cp != '%') {
525 save_char(UChar(*cp));
526 } else {
527 tparam_base = cp++;
528 cp = parse_format(cp, fmt_buff, &len);
529 switch (*cp) {
530 default:
531 break;
532 case '%':
533 save_char('%');
534 break;
536 case 'd': /* FALLTHRU */
537 case 'o': /* FALLTHRU */
538 case 'x': /* FALLTHRU */
539 case 'X': /* FALLTHRU */
540 save_number(fmt_buff, npop(), len);
541 break;
543 case 'c': /* FALLTHRU */
544 save_char(npop());
545 break;
547 case 'l':
548 save_number("%d", (int) grub_strlen(spop()), 0);
549 break;
551 case 's':
552 save_text(fmt_buff, spop(), len);
553 break;
555 case 'p':
556 cp++;
557 i = (UChar(*cp) - '1');
558 if (i >= 0 && i < NUM_PARM) {
559 if (p_is_s[i])
560 spush(p_is_s[i]);
561 else
562 npush(param[i]);
564 break;
566 case 'P':
567 cp++;
568 if (isUPPER(*cp)) {
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();
575 break;
577 case 'g':
578 cp++;
579 if (isUPPER(*cp)) {
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]);
586 break;
588 case '\'':
589 cp++;
590 npush(UChar(*cp));
591 cp++;
592 break;
594 case '{':
595 number = 0;
596 cp++;
597 while (isdigit(UChar(*cp))) {
598 number = (number * 10) + (UChar(*cp) - '0');
599 cp++;
601 npush(number);
602 break;
604 case '+':
605 npush(npop() + npop());
606 break;
608 case '-':
609 y = npop();
610 x = npop();
611 npush(x - y);
612 break;
614 case '*':
615 npush(npop() * npop());
616 break;
618 case '/':
619 y = npop();
620 x = npop();
621 /* GRUB has no signed divisions. */
622 npush(y ? ((unsigned)x / (unsigned)y) : 0);
623 break;
625 case 'm':
626 y = npop();
627 x = npop();
628 /* GRUB has no signed divisions. */
629 npush(y ? ((unsigned)x % (unsigned)y) : 0);
630 break;
632 case 'A':
633 npush(npop() && npop());
634 break;
636 case 'O':
637 npush(npop() || npop());
638 break;
640 case '&':
641 npush(npop() & npop());
642 break;
644 case '|':
645 npush(npop() | npop());
646 break;
648 case '^':
649 npush(npop() ^ npop());
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 y = npop();
666 x = npop();
667 npush(x > y);
668 break;
670 case '!':
671 npush(!npop());
672 break;
674 case '~':
675 npush(~npop());
676 break;
678 case 'i':
679 if (p_is_s[0] == 0)
680 param[0]++;
681 if (p_is_s[1] == 0)
682 param[1]++;
683 break;
685 case '?':
686 break;
688 case 't':
689 x = npop();
690 if (!x) {
691 /* scan forward for %e or %; at level zero */
692 cp++;
693 level = 0;
694 while (*cp) {
695 if (*cp == '%') {
696 cp++;
697 if (*cp == '?')
698 level++;
699 else if (*cp == ';') {
700 if (level > 0)
701 level--;
702 else
703 break;
704 } else if (*cp == 'e' && level == 0)
705 break;
708 if (*cp)
709 cp++;
712 break;
714 case 'e':
715 /* scan forward for a %; at level zero */
716 cp++;
717 level = 0;
718 while (*cp) {
719 if (*cp == '%') {
720 cp++;
721 if (*cp == '?')
722 level++;
723 else if (*cp == ';') {
724 if (level > 0)
725 level--;
726 else
727 break;
731 if (*cp)
732 cp++;
734 break;
736 case ';':
737 break;
739 } /* endswitch (*cp) */
740 } /* endelse (*cp == '%') */
742 if (*cp == '\0')
743 break;
745 cp++;
746 } /* endwhile (*cp) */
748 get_space(1);
749 out_buff[out_used] = '\0';
751 return (out_buff);
754 const char *
755 grub_terminfo_tparm (const char *string, ...)
757 va_list ap;
758 char *result;
760 if (!string)
761 return "";
763 va_start (ap, string);
764 result = tparam_internal (string, ap);
765 va_end (ap);
766 return result;