2008-04-04 Pavel Roskin <proski@gnu.org>
[grub2/bean.git] / term / tparm.c
blob55955b639d817e37c5d91c2fdbbee00dbb1f8037
1 /****************************************************************************
2 * Copyright (c) 1998-2003,2004,2005 Free Software Foundation, Inc. *
3 * *
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: *
11 * *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
14 * *
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. *
22 * *
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 *
26 * authorization. *
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 ****************************************************************************/
47 * tparm.c
51 #include <grub/misc.h>
52 #include <grub/mm.h>
53 #include <grub/types.h>
54 #include <grub/tparm.h>
57 * Common/troublesome character definitions
59 typedef char grub_bool_t;
60 #ifndef FALSE
61 # define FALSE (0)
62 #endif
63 #ifndef TRUE
64 # define TRUE (!FALSE)
65 #endif
67 #define NUM_PARM 9
68 #define NUM_VARS 26
69 #define STACKSIZE 20
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))
79 //MODULE_ID("$Id$")
82 * char *
83 * tparm(string, ...)
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
96 * be indicated by
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:
105 * %% outputs `%'
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
122 * %+ %- %* %/ %m
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
137 * %gx %gy %m
138 * resulting in x mod y, not the reverse.
141 typedef struct {
142 union {
143 int num;
144 char *str;
145 } data;
146 grub_bool_t num_type;
147 } stack_frame;
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;
160 static inline void
161 get_space(grub_size_t need)
163 need += out_used;
164 if (need > out_size) {
165 out_size = need * 2;
166 out_buff = grub_realloc(out_buff, out_size*sizeof(char));
167 /* FIX ME! handle out_buff == 0. */
171 static inline void
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)
176 s_len = 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);
184 static inline void
185 save_number(const char *fmt, int number, int len)
187 if (len < 30)
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);
196 static inline void
197 save_char(int c)
199 if (c == 0)
200 c = 0200;
201 get_space(1);
202 out_buff[out_used++] = c;
205 static inline void
206 npush(int x)
208 if (stack_ptr < STACKSIZE) {
209 stack[stack_ptr].num_type = TRUE;
210 stack[stack_ptr].data.num = x;
211 stack_ptr++;
215 static inline int
216 npop(void)
218 int result = 0;
219 if (stack_ptr > 0) {
220 stack_ptr--;
221 if (stack[stack_ptr].num_type)
222 result = stack[stack_ptr].data.num;
224 return result;
227 static inline void
228 spush(char *x)
230 if (stack_ptr < STACKSIZE) {
231 stack[stack_ptr].num_type = FALSE;
232 stack[stack_ptr].data.str = x;
233 stack_ptr++;
237 static inline char *
238 spop(void)
240 static char dummy[] = ""; /* avoid const-cast */
241 char *result = dummy;
242 if (stack_ptr > 0) {
243 stack_ptr--;
244 if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
245 result = stack[stack_ptr].data.str;
247 return result;
250 static inline const char *
251 parse_format(const char *s, char *format, int *len)
253 *len = 0;
254 if (format != 0) {
255 grub_bool_t done = FALSE;
256 grub_bool_t allowminus = FALSE;
257 grub_bool_t dot = FALSE;
258 grub_bool_t err = FALSE;
259 char *fmt = format;
260 int my_width = 0;
261 int my_prec = 0;
262 int value = 0;
264 *len = 0;
265 *format++ = '%';
266 while (*s != '\0' && !done) {
267 switch (*s) {
268 case 'c': /* FALLTHRU */
269 case 'd': /* FALLTHRU */
270 case 'o': /* FALLTHRU */
271 case 'x': /* FALLTHRU */
272 case 'X': /* FALLTHRU */
273 case 's':
274 *format++ = *s;
275 done = TRUE;
276 break;
277 case '.':
278 *format++ = *s++;
279 if (dot) {
280 err = TRUE;
281 } else { /* value before '.' is the width */
282 dot = TRUE;
283 my_width = value;
285 value = 0;
286 break;
287 case '#':
288 *format++ = *s++;
289 break;
290 case ' ':
291 *format++ = *s++;
292 break;
293 case ':':
294 s++;
295 allowminus = TRUE;
296 break;
297 case '-':
298 if (allowminus) {
299 *format++ = *s++;
300 } else {
301 done = TRUE;
303 break;
304 default:
305 if (isdigit(UChar(*s))) {
306 value = (value * 10) + (*s - '0');
307 if (value > 10000)
308 err = TRUE;
309 *format++ = *s++;
310 } else {
311 done = TRUE;
317 * If we found an error, ignore (and remove) the flags.
319 if (err) {
320 my_width = my_prec = value = 0;
321 format = fmt;
322 *format++ = '%';
323 *format++ = *s;
327 * Any value after '.' is the precision. If we did not see '.', then
328 * the value is the width.
330 if (dot)
331 my_prec = value;
332 else
333 my_width = value;
335 *format = '\0';
336 /* return maximum string length in print */
337 *len = (my_width > my_prec) ? my_width : my_prec;
339 return s;
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.
353 static inline int
354 analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
356 grub_size_t len2;
357 int i;
358 int lastpop = -1;
359 int len;
360 int number = 0;
361 const char *cp = string;
362 static char dummy[] = "";
364 if (cp == 0)
365 return 0;
367 if ((len2 = grub_strlen(cp)) > fmt_size) {
368 fmt_size = len2 + fmt_size + 2;
369 if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
370 return 0;
373 grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
374 *popcount = 0;
376 while ((cp - string) < (int) len2) {
377 if (*cp == '%') {
378 cp++;
379 cp = parse_format(cp, fmt_buff, &len);
380 switch (*cp) {
381 default:
382 break;
384 case 'd': /* FALLTHRU */
385 case 'o': /* FALLTHRU */
386 case 'x': /* FALLTHRU */
387 case 'X': /* FALLTHRU */
388 case 'c': /* FALLTHRU */
389 if (lastpop <= 0)
390 number++;
391 lastpop = -1;
392 break;
394 case 'l':
395 case 's':
396 if (lastpop > 0)
397 p_is_s[lastpop - 1] = dummy;
398 ++number;
399 break;
401 case 'p':
402 cp++;
403 i = (UChar(*cp) - '0');
404 if (i >= 0 && i <= NUM_PARM) {
405 lastpop = i;
406 if (lastpop > *popcount)
407 *popcount = lastpop;
409 break;
411 case 'P':
412 ++number;
413 ++cp;
414 break;
416 case 'g':
417 cp++;
418 break;
420 case '\'':
421 cp += 2;
422 lastpop = -1;
423 break;
425 case '{':
426 cp++;
427 while (isdigit(UChar(*cp))) {
428 cp++;
430 break;
432 case '+':
433 case '-':
434 case '*':
435 case '/':
436 case 'm':
437 case 'A':
438 case 'O':
439 case '&':
440 case '|':
441 case '^':
442 case '=':
443 case '<':
444 case '>':
445 lastpop = -1;
446 number += 2;
447 break;
449 case '!':
450 case '~':
451 lastpop = -1;
452 ++number;
453 break;
455 case 'i':
456 /* will add 1 to first (usually two) parameters */
457 break;
460 if (*cp != '\0')
461 cp++;
464 if (number > NUM_PARM)
465 number = NUM_PARM;
466 return number;
469 static inline char *
470 tparam_internal(const char *string, va_list ap)
472 char *p_is_s[NUM_PARM];
473 long param[NUM_PARM];
474 int popcount;
475 int number;
476 int len;
477 int level;
478 int x, y;
479 int i;
480 const char *cp = string;
481 grub_size_t len2;
482 static int dynamic_var[NUM_VARS];
483 static int static_vars[NUM_VARS];
485 if (cp == 0)
486 return 0;
488 out_used = out_size = fmt_size = 0;
490 len2 = (int) grub_strlen(cp);
493 * Find the highest parameter-number referred to in the format string.
494 * Use this value to limit the number of arguments copied from the
495 * variable-length argument list.
497 number = analyze(cp, p_is_s, &popcount);
498 if (fmt_buff == 0)
499 return 0;
501 for (i = 0; i < max(popcount, number); i++) {
503 * A few caps (such as plab_norm) have string-valued parms.
504 * We'll have to assume that the caller knows the difference, since
505 * a char* and an int may not be the same size on the stack.
507 if (p_is_s[i] != 0) {
508 p_is_s[i] = va_arg(ap, char *);
509 } else {
510 param[i] = va_arg(ap, long int);
515 * This is a termcap compatibility hack. If there are no explicit pop
516 * operations in the string, load the stack in such a way that
517 * successive pops will grab successive parameters. That will make
518 * the expansion of (for example) \E[%d;%dH work correctly in termcap
519 * style, which means tparam() will expand termcap strings OK.
521 stack_ptr = 0;
522 if (popcount == 0) {
523 popcount = number;
524 for (i = number - 1; i >= 0; i--)
525 npush(param[i]);
528 while ((cp - string) < (int) len2) {
529 if (*cp != '%') {
530 save_char(UChar(*cp));
531 } else {
532 tparam_base = cp++;
533 cp = parse_format(cp, fmt_buff, &len);
534 switch (*cp) {
535 default:
536 break;
537 case '%':
538 save_char('%');
539 break;
541 case 'd': /* FALLTHRU */
542 case 'o': /* FALLTHRU */
543 case 'x': /* FALLTHRU */
544 case 'X': /* FALLTHRU */
545 save_number(fmt_buff, npop(), len);
546 break;
548 case 'c': /* FALLTHRU */
549 save_char(npop());
550 break;
552 case 'l':
553 save_number("%d", (int) grub_strlen(spop()), 0);
554 break;
556 case 's':
557 save_text(fmt_buff, spop(), len);
558 break;
560 case 'p':
561 cp++;
562 i = (UChar(*cp) - '1');
563 if (i >= 0 && i < NUM_PARM) {
564 if (p_is_s[i])
565 spush(p_is_s[i]);
566 else
567 npush(param[i]);
569 break;
571 case 'P':
572 cp++;
573 if (isUPPER(*cp)) {
574 i = (UChar(*cp) - 'A');
575 static_vars[i] = npop();
576 } else if (isLOWER(*cp)) {
577 i = (UChar(*cp) - 'a');
578 dynamic_var[i] = npop();
580 break;
582 case 'g':
583 cp++;
584 if (isUPPER(*cp)) {
585 i = (UChar(*cp) - 'A');
586 npush(static_vars[i]);
587 } else if (isLOWER(*cp)) {
588 i = (UChar(*cp) - 'a');
589 npush(dynamic_var[i]);
591 break;
593 case '\'':
594 cp++;
595 npush(UChar(*cp));
596 cp++;
597 break;
599 case '{':
600 number = 0;
601 cp++;
602 while (isdigit(UChar(*cp))) {
603 number = (number * 10) + (UChar(*cp) - '0');
604 cp++;
606 npush(number);
607 break;
609 case '+':
610 npush(npop() + npop());
611 break;
613 case '-':
614 y = npop();
615 x = npop();
616 npush(x - y);
617 break;
619 case '*':
620 npush(npop() * npop());
621 break;
623 case '/':
624 y = npop();
625 x = npop();
626 npush(y ? (x / y) : 0);
627 break;
629 case 'm':
630 y = npop();
631 x = npop();
632 npush(y ? (x % y) : 0);
633 break;
635 case 'A':
636 npush(npop() && npop());
637 break;
639 case 'O':
640 npush(npop() || npop());
641 break;
643 case '&':
644 npush(npop() & npop());
645 break;
647 case '|':
648 npush(npop() | npop());
649 break;
651 case '^':
652 npush(npop() ^ npop());
653 break;
655 case '=':
656 y = npop();
657 x = npop();
658 npush(x == y);
659 break;
661 case '<':
662 y = npop();
663 x = npop();
664 npush(x < y);
665 break;
667 case '>':
668 y = npop();
669 x = npop();
670 npush(x > y);
671 break;
673 case '!':
674 npush(!npop());
675 break;
677 case '~':
678 npush(~npop());
679 break;
681 case 'i':
682 if (p_is_s[0] == 0)
683 param[0]++;
684 if (p_is_s[1] == 0)
685 param[1]++;
686 break;
688 case '?':
689 break;
691 case 't':
692 x = npop();
693 if (!x) {
694 /* scan forward for %e or %; at level zero */
695 cp++;
696 level = 0;
697 while (*cp) {
698 if (*cp == '%') {
699 cp++;
700 if (*cp == '?')
701 level++;
702 else if (*cp == ';') {
703 if (level > 0)
704 level--;
705 else
706 break;
707 } else if (*cp == 'e' && level == 0)
708 break;
711 if (*cp)
712 cp++;
715 break;
717 case 'e':
718 /* scan forward for a %; at level zero */
719 cp++;
720 level = 0;
721 while (*cp) {
722 if (*cp == '%') {
723 cp++;
724 if (*cp == '?')
725 level++;
726 else if (*cp == ';') {
727 if (level > 0)
728 level--;
729 else
730 break;
734 if (*cp)
735 cp++;
737 break;
739 case ';':
740 break;
742 } /* endswitch (*cp) */
743 } /* endelse (*cp == '%') */
745 if (*cp == '\0')
746 break;
748 cp++;
749 } /* endwhile (*cp) */
751 get_space(1);
752 out_buff[out_used] = '\0';
754 return (out_buff);
757 char *
758 grub_terminfo_tparm (const char *string, ...)
760 va_list ap;
761 char *result;
763 va_start (ap, string);
764 result = tparam_internal (string, ap);
765 va_end (ap);
766 return result;