2008-11-08 Robert Millan <rmh@aybabtu.com>
[grub2/phcoder.git] / term / tparm.c
blobe76cbe7234581d276d6655f5132cca3f30be9001
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 *popcount = 0;
366 if (cp == 0)
367 return 0;
369 if ((len2 = grub_strlen(cp)) > fmt_size) {
370 fmt_size = len2 + fmt_size + 2;
371 if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
372 return 0;
375 grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
377 while ((cp - string) < (int) len2) {
378 if (*cp == '%') {
379 cp++;
380 cp = parse_format(cp, fmt_buff, &len);
381 switch (*cp) {
382 default:
383 break;
385 case 'd': /* FALLTHRU */
386 case 'o': /* FALLTHRU */
387 case 'x': /* FALLTHRU */
388 case 'X': /* FALLTHRU */
389 case 'c': /* FALLTHRU */
390 if (lastpop <= 0)
391 number++;
392 lastpop = -1;
393 break;
395 case 'l':
396 case 's':
397 if (lastpop > 0)
398 p_is_s[lastpop - 1] = dummy;
399 ++number;
400 break;
402 case 'p':
403 cp++;
404 i = (UChar(*cp) - '0');
405 if (i >= 0 && i <= NUM_PARM) {
406 lastpop = i;
407 if (lastpop > *popcount)
408 *popcount = lastpop;
410 break;
412 case 'P':
413 ++number;
414 ++cp;
415 break;
417 case 'g':
418 cp++;
419 break;
421 case '\'':
422 cp += 2;
423 lastpop = -1;
424 break;
426 case '{':
427 cp++;
428 while (isdigit(UChar(*cp))) {
429 cp++;
431 break;
433 case '+':
434 case '-':
435 case '*':
436 case '/':
437 case 'm':
438 case 'A':
439 case 'O':
440 case '&':
441 case '|':
442 case '^':
443 case '=':
444 case '<':
445 case '>':
446 lastpop = -1;
447 number += 2;
448 break;
450 case '!':
451 case '~':
452 lastpop = -1;
453 ++number;
454 break;
456 case 'i':
457 /* will add 1 to first (usually two) parameters */
458 break;
461 if (*cp != '\0')
462 cp++;
465 if (number > NUM_PARM)
466 number = NUM_PARM;
467 return number;
470 static inline char *
471 tparam_internal(const char *string, va_list ap)
473 char *p_is_s[NUM_PARM];
474 long param[NUM_PARM];
475 int popcount;
476 int number;
477 int len;
478 int level;
479 int x, y;
480 int i;
481 const char *cp = string;
482 grub_size_t len2;
483 static int dynamic_var[NUM_VARS];
484 static int static_vars[NUM_VARS];
486 if (cp == 0)
487 return 0;
489 out_used = out_size = fmt_size = 0;
491 len2 = (int) grub_strlen(cp);
494 * Find the highest parameter-number referred to in the format string.
495 * Use this value to limit the number of arguments copied from the
496 * variable-length argument list.
498 number = analyze(cp, p_is_s, &popcount);
499 if (fmt_buff == 0)
500 return 0;
502 for (i = 0; i < max(popcount, number); i++) {
504 * A few caps (such as plab_norm) have string-valued parms.
505 * We'll have to assume that the caller knows the difference, since
506 * a char* and an int may not be the same size on the stack.
508 if (p_is_s[i] != 0) {
509 p_is_s[i] = va_arg(ap, char *);
510 } else {
511 param[i] = va_arg(ap, long int);
516 * This is a termcap compatibility hack. If there are no explicit pop
517 * operations in the string, load the stack in such a way that
518 * successive pops will grab successive parameters. That will make
519 * the expansion of (for example) \E[%d;%dH work correctly in termcap
520 * style, which means tparam() will expand termcap strings OK.
522 stack_ptr = 0;
523 if (popcount == 0) {
524 popcount = number;
525 for (i = number - 1; i >= 0; i--)
526 npush(param[i]);
529 while ((cp - string) < (int) len2) {
530 if (*cp != '%') {
531 save_char(UChar(*cp));
532 } else {
533 tparam_base = cp++;
534 cp = parse_format(cp, fmt_buff, &len);
535 switch (*cp) {
536 default:
537 break;
538 case '%':
539 save_char('%');
540 break;
542 case 'd': /* FALLTHRU */
543 case 'o': /* FALLTHRU */
544 case 'x': /* FALLTHRU */
545 case 'X': /* FALLTHRU */
546 save_number(fmt_buff, npop(), len);
547 break;
549 case 'c': /* FALLTHRU */
550 save_char(npop());
551 break;
553 case 'l':
554 save_number("%d", (int) grub_strlen(spop()), 0);
555 break;
557 case 's':
558 save_text(fmt_buff, spop(), len);
559 break;
561 case 'p':
562 cp++;
563 i = (UChar(*cp) - '1');
564 if (i >= 0 && i < NUM_PARM) {
565 if (p_is_s[i])
566 spush(p_is_s[i]);
567 else
568 npush(param[i]);
570 break;
572 case 'P':
573 cp++;
574 if (isUPPER(*cp)) {
575 i = (UChar(*cp) - 'A');
576 static_vars[i] = npop();
577 } else if (isLOWER(*cp)) {
578 i = (UChar(*cp) - 'a');
579 dynamic_var[i] = npop();
581 break;
583 case 'g':
584 cp++;
585 if (isUPPER(*cp)) {
586 i = (UChar(*cp) - 'A');
587 npush(static_vars[i]);
588 } else if (isLOWER(*cp)) {
589 i = (UChar(*cp) - 'a');
590 npush(dynamic_var[i]);
592 break;
594 case '\'':
595 cp++;
596 npush(UChar(*cp));
597 cp++;
598 break;
600 case '{':
601 number = 0;
602 cp++;
603 while (isdigit(UChar(*cp))) {
604 number = (number * 10) + (UChar(*cp) - '0');
605 cp++;
607 npush(number);
608 break;
610 case '+':
611 npush(npop() + npop());
612 break;
614 case '-':
615 y = npop();
616 x = npop();
617 npush(x - y);
618 break;
620 case '*':
621 npush(npop() * npop());
622 break;
624 case '/':
625 y = npop();
626 x = npop();
627 npush(y ? (x / y) : 0);
628 break;
630 case 'm':
631 y = npop();
632 x = npop();
633 npush(y ? (x % y) : 0);
634 break;
636 case 'A':
637 npush(npop() && npop());
638 break;
640 case 'O':
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 npush(npop() ^ npop());
654 break;
656 case '=':
657 y = npop();
658 x = npop();
659 npush(x == y);
660 break;
662 case '<':
663 y = npop();
664 x = npop();
665 npush(x < y);
666 break;
668 case '>':
669 y = npop();
670 x = npop();
671 npush(x > y);
672 break;
674 case '!':
675 npush(!npop());
676 break;
678 case '~':
679 npush(~npop());
680 break;
682 case 'i':
683 if (p_is_s[0] == 0)
684 param[0]++;
685 if (p_is_s[1] == 0)
686 param[1]++;
687 break;
689 case '?':
690 break;
692 case 't':
693 x = npop();
694 if (!x) {
695 /* scan forward for %e or %; at level zero */
696 cp++;
697 level = 0;
698 while (*cp) {
699 if (*cp == '%') {
700 cp++;
701 if (*cp == '?')
702 level++;
703 else if (*cp == ';') {
704 if (level > 0)
705 level--;
706 else
707 break;
708 } else if (*cp == 'e' && level == 0)
709 break;
712 if (*cp)
713 cp++;
716 break;
718 case 'e':
719 /* scan forward for a %; at level zero */
720 cp++;
721 level = 0;
722 while (*cp) {
723 if (*cp == '%') {
724 cp++;
725 if (*cp == '?')
726 level++;
727 else if (*cp == ';') {
728 if (level > 0)
729 level--;
730 else
731 break;
735 if (*cp)
736 cp++;
738 break;
740 case ';':
741 break;
743 } /* endswitch (*cp) */
744 } /* endelse (*cp == '%') */
746 if (*cp == '\0')
747 break;
749 cp++;
750 } /* endwhile (*cp) */
752 get_space(1);
753 out_buff[out_used] = '\0';
755 return (out_buff);
758 char *
759 grub_terminfo_tparm (const char *string, ...)
761 va_list ap;
762 char *result;
764 va_start (ap, string);
765 result = tparam_internal (string, ap);
766 va_end (ap);
767 return result;