1 /****************************************************************************
2 * Copyright (c) 1998,2000,2002 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 ****************************************************************************/
28
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.
34 *
35 * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002
36 **********************************************************************/
37
38 /****************************************************************************
39 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
40 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
41 ****************************************************************************/
42
43 /*
44 * tparm.c
45 *
46 */
47
48 #include "shared.h"
49
50 #include "tparm.h"
51
52 /*
53 * Common/troublesome character definitions
54 */
55 typedef char grub_bool;
56 #undef isdigit
57 #define isdigit(c) ((c) >= '0' && (c) <= '9')
58 #ifndef FALSE
59 # define FALSE (0)
60 #endif
61 #ifndef TRUE
62 # define TRUE (!FALSE)
63 #endif
64 #define MAX_FORMAT_LEN 256
65 #define max(a,b) ((a) > (b) ? (a) : (b))
66
67 //MODULE_ID("$Id: tparm.c,v 1.1.1.1 2003/11/20 02:04:59 fengshuo Exp $")
68
69 /*
70 * char *
71 * tparm(string, ...)
72 *
73 * Substitute the given parameters into the given string by the following
74 * rules (taken from terminfo(7)):
75 *
76 * Cursor addressing and other strings requiring parame-
77 * ters in the terminal are described by a parameterized string
78 * capability, with like escapes %x in it. For example, to
79 * address the cursor, the cup capability is given, using two
80 * parameters: the row and column to address to. (Rows and
81 * columns are numbered from zero and refer to the physical
82 * screen visible to the user, not to any unseen memory.) If
83 * the terminal has memory relative cursor addressing, that can
84 * be indicated by
85 *
86 * The parameter mechanism uses a stack and special %
87 * codes to manipulate it. Typically a sequence will push one
88 * of the parameters onto the stack and then print it in some
89 * format. Often more complex operations are necessary.
90 *
91 * The % encodings have the following meanings:
92 *
93 * %% outputs `%'
94 * %c print pop() like %c in printf()
95 * %s print pop() like %s in printf()
96 * %[[:]flags][width[.precision]][doxXs]
97 * as in printf, flags are [-+#] and space
98 * The ':' is used to avoid making %+ or %-
99 * patterns (see below).
100 *
101 * %p[1-9] push ith parm
102 * %P[a-z] set dynamic variable [a-z] to pop()
103 * %g[a-z] get dynamic variable [a-z] and push it
104 * %P[A-Z] set static variable [A-Z] to pop()
105 * %g[A-Z] get static variable [A-Z] and push it
106 * %l push strlen(pop)
107 * %'c' push char constant c
108 * %{nn} push integer constant nn
109 *
110 * %+ %- %* %/ %m
111 * arithmetic (%m is mod): push(pop() op pop())
112 * %& %| %^ bit operations: push(pop() op pop())
113 * %= %> %< logical operations: push(pop() op pop())
114 * %A %O logical and & or operations for conditionals
115 * %! %~ unary operations push(op pop())
116 * %i add 1 to first two parms (for ANSI terminals)
117 *
118 * %? expr %t thenpart %e elsepart %;
119 * if-then-else, %e elsepart is optional.
120 * else-if's are possible ala Algol 68:
121 * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
122 *
123 * For those of the above operators which are binary and not commutative,
124 * the stack works in the usual way, with
125 * %gx %gy %m
126 * resulting in x mod y, not the reverse.
127 */
128
129 #define STACKSIZE 20
130
131 typedef struct {
132 union {
133 unsigned int num;
134 char *str;
135 } data;
136 grub_bool num_type;
137 } stack_frame;
138
139 static stack_frame stack[STACKSIZE];
140 static int stack_ptr;
141
142 static char out_buff[256];
143 static int out_size = 256;
144 static int out_used;
145
146 static inline void
get_space(int need)147 get_space(int need)
148 {
149 need += out_used;
150 if (need > out_size) {
151 // FIX ME! buffer full, what now?
152 ;
153 }
154 }
155
156 static inline void
save_text(const char * fmt,const char * s,int len)157 save_text(const char *fmt, const char *s, int len)
158 {
159 int s_len = grub_strlen(s);
160 if (len > (int) s_len)
161 s_len = len;
162
163 get_space(s_len + 1);
164
165 (void) grub_sprintf(out_buff + out_used, fmt, s);
166 out_used += grub_strlen(out_buff + out_used);
167 }
168
169 static inline void
save_number(const char * fmt,int number,int len)170 save_number(const char *fmt, int number, int len)
171 {
172 if (len < 30)
173 len = 30; /* actually log10(MAX_INT)+1 */
174
175 get_space(len + 1);
176
177 (void) grub_sprintf(out_buff + out_used, fmt, number);
178 out_used += grub_strlen(out_buff + out_used);
179 }
180
181 static inline void
save_char(int c)182 save_char(int c)
183 {
184 if (c == 0)
185 c = 0200;
186 get_space(1);
187 out_buff[out_used++] = c;
188 }
189
190 static inline void
npush(int x)191 npush(int x)
192 {
193 if (stack_ptr < STACKSIZE) {
194 stack[stack_ptr].num_type = TRUE;
195 stack[stack_ptr].data.num = x;
196 stack_ptr++;
197 }
198 }
199
200 static inline int
npop(void)201 npop(void)
202 {
203 int result = 0;
204 if (stack_ptr > 0) {
205 stack_ptr--;
206 if (stack[stack_ptr].num_type)
207 result = stack[stack_ptr].data.num;
208 }
209 return result;
210 }
211
212 static inline void
spush(char * x)213 spush(char *x)
214 {
215 if (stack_ptr < STACKSIZE) {
216 stack[stack_ptr].num_type = FALSE;
217 stack[stack_ptr].data.str = x;
218 stack_ptr++;
219 }
220 }
221
222 static inline char *
spop(void)223 spop(void)
224 {
225 static char dummy[] = ""; /* avoid const-cast */
226 char *result = dummy;
227 if (stack_ptr > 0) {
228 stack_ptr--;
229 if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
230 result = stack[stack_ptr].data.str;
231 }
232 return result;
233 }
234
235 static inline const char *
parse_format(const char * s,char * format,int * len)236 parse_format(const char *s, char *format, int *len)
237 {
238 grub_bool done = FALSE;
239 grub_bool allowminus = FALSE;
240 grub_bool dot = FALSE;
241 grub_bool err = FALSE;
242 char *fmt = format;
243 int prec = 0;
244 int width = 0;
245 int value = 0;
246
247 *len = 0;
248 *format++ = '%';
249 while (*s != '\0' && !done) {
250 switch (*s) {
251 case 'c': /* FALLTHRU */
252 case 'd': /* FALLTHRU */
253 case 'o': /* FALLTHRU */
254 case 'x': /* FALLTHRU */
255 case 'X': /* FALLTHRU */
256 case 's':
257 *format++ = *s;
258 done = TRUE;
259 break;
260 case '.':
261 *format++ = *s++;
262 if (dot) {
263 err = TRUE;
264 } else {
265 dot = TRUE;
266 prec = value;
267 }
268 value = 0;
269 break;
270 case '#':
271 *format++ = *s++;
272 break;
273 case ' ':
274 *format++ = *s++;
275 break;
276 case ':':
277 s++;
278 allowminus = TRUE;
279 break;
280 case '-':
281 if (allowminus) {
282 *format++ = *s++;
283 } else {
284 done = TRUE;
285 }
286 break;
287 default:
288 if (isdigit(*s)) {
289 value = (value * 10) + (*s - '0');
290 if (value > 10000)
291 err = TRUE;
292 *format++ = *s++;
293 } else {
294 done = TRUE;
295 }
296 }
297 }
298
299 /*
300 * If we found an error, ignore (and remove) the flags.
301 */
302 if (err) {
303 prec = width = value = 0;
304 format = fmt;
305 *format++ = '%';
306 *format++ = *s;
307 }
308
309 if (dot)
310 width = value;
311 else
312 prec = value;
313
314 *format = '\0';
315 /* return maximum string length in print */
316 *len = (prec > width) ? prec : width;
317 return s;
318 }
319
320 #define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
321 #define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
322
323 static inline char *
tparam_internal(const char * string,int * dataptr)324 tparam_internal(const char *string, int *dataptr)
325 {
326 #define NUM_VARS 26
327 char *p_is_s[9];
328 int param[9];
329 int lastpop;
330 int popcount;
331 int number;
332 int len;
333 int level;
334 int x, y;
335 int i;
336 int len2;
337 register const char *cp;
338 static int len_fmt = MAX_FORMAT_LEN;
339 static char dummy[] = "";
340 static char format[MAX_FORMAT_LEN];
341 static int dynamic_var[NUM_VARS];
342 static int static_vars[NUM_VARS];
343
344 out_used = 0;
345 if (string == NULL)
346 return NULL;
347
348 if ((len2 = grub_strlen(string)) > len_fmt) {
349 return NULL;
350 }
351
352 /*
353 * Find the highest parameter-number referred to in the format string.
354 * Use this value to limit the number of arguments copied from the
355 * variable-length argument list.
356 */
357
358 number = 0;
359 lastpop = -1;
360 popcount = 0;
361 grub_memset(p_is_s, 0, sizeof(p_is_s));
362
363 /*
364 * Analyze the string to see how many parameters we need from the varargs
365 * list, and what their types are. We will only accept string parameters
366 * if they appear as a %l or %s format following an explicit parameter
367 * reference (e.g., %p2%s). All other parameters are numbers.
368 *
369 * 'number' counts coarsely the number of pop's we see in the string, and
370 * 'popcount' shows the highest parameter number in the string. We would
371 * like to simply use the latter count, but if we are reading termcap
372 * strings, there may be cases that we cannot see the explicit parameter
373 * numbers.
374 */
375 for (cp = string; (cp - string) < (int) len2;) {
376 if (*cp == '%') {
377 cp++;
378 cp = parse_format(cp, format, &len);
379 switch (*cp) {
380 default:
381 break;
382
383 case 'd': /* FALLTHRU */
384 case 'o': /* FALLTHRU */
385 case 'x': /* FALLTHRU */
386 case 'X': /* FALLTHRU */
387 case 'c': /* FALLTHRU */
388 number++;
389 lastpop = -1;
390 break;
391
392 case 'l':
393 case 's':
394 if (lastpop > 0)
395 p_is_s[lastpop - 1] = dummy;
396 ++number;
397 break;
398
399 case 'p':
400 cp++;
401 i = (*cp - '0');
402 if (i >= 0 && i <= 9) {
403 lastpop = i;
404 if (lastpop > popcount)
405 popcount = lastpop;
406 }
407 break;
408
409 case 'P':
410 case 'g':
411 cp++;
412 break;
413
414 case '\'':
415 cp += 2;
416 lastpop = -1;
417 break;
418
419 case '{':
420 cp++;
421 while (*cp >= '0' && *cp <= '9') {
422 cp++;
423 }
424 break;
425
426 case '+':
427 case '-':
428 case '*':
429 case '/':
430 case 'm':
431 case 'A':
432 case 'O':
433 case '&':
434 case '|':
435 case '^':
436 case '=':
437 case '<':
438 case '>':
439 case '!':
440 case '~':
441 lastpop = -1;
442 number += 2;
443 break;
444
445 case 'i':
446 lastpop = -1;
447 if (popcount < 2)
448 popcount = 2;
449 break;
450 }
451 }
452 if (*cp != '\0')
453 cp++;
454 }
455
456 if (number > 9)
457 number = 9;
458 for (i = 0; i < max(popcount, number); i++) {
459 /*
460 * A few caps (such as plab_norm) have string-valued parms.
461 * We'll have to assume that the caller knows the difference, since
462 * a char* and an int may not be the same size on the stack.
463 */
464 if (p_is_s[i] != 0) {
465 p_is_s[i] = (char *)(*(dataptr++));
466 } else {
467 param[i] = (int)(*(dataptr++));
468 }
469 }
470
471 /*
472 * This is a termcap compatibility hack. If there are no explicit pop
473 * operations in the string, load the stack in such a way that
474 * successive pops will grab successive parameters. That will make
475 * the expansion of (for example) \E[%d;%dH work correctly in termcap
476 * style, which means tparam() will expand termcap strings OK.
477 */
478 stack_ptr = 0;
479 if (popcount == 0) {
480 popcount = number;
481 for (i = number - 1; i >= 0; i--)
482 npush(param[i]);
483 }
484
485 while (*string) {
486 /* skip delay timings */
487 if (*string == '$' && *(string + 1) == '<') {
488 while( *string && *string != '>')
489 string++;
490 if ( *string == '>' ) string++;
491 } else if ( *string == '%') {
492 string++;
493 string = parse_format(string, format, &len);
494 switch (*string) {
495 default:
496 break;
497 case '%':
498 save_char('%');
499 break;
500
501 case 'd': /* FALLTHRU */
502 case 'o': /* FALLTHRU */
503 case 'x': /* FALLTHRU */
504 case 'X': /* FALLTHRU */
505 case 'c': /* FALLTHRU */
506 save_number(format, npop(), len);
507 break;
508
509 case 'l':
510 save_number("%d", strlen(spop()), 0);
511 break;
512
513 case 's':
514 save_text(format, spop(), len);
515 break;
516
517 case 'p':
518 string++;
519 i = (*string - '1');
520 if (i >= 0 && i < 9) {
521 if (p_is_s[i])
522 spush(p_is_s[i]);
523 else
524 npush(param[i]);
525 }
526 break;
527
528 case 'P':
529 string++;
530 if (isUPPER(*string)) {
531 i = (*string - 'A');
532 static_vars[i] = npop();
533 } else if (isLOWER(*string)) {
534 i = (*string - 'a');
535 dynamic_var[i] = npop();
536 }
537 break;
538
539 case 'g':
540 string++;
541 if (isUPPER(*string)) {
542 i = (*string - 'A');
543 npush(static_vars[i]);
544 } else if (isLOWER(*string)) {
545 i = (*string - 'a');
546 npush(dynamic_var[i]);
547 }
548 break;
549
550 case '\'':
551 string++;
552 npush(*string);
553 string++;
554 break;
555
556 case '{':
557 number = 0;
558 string++;
559 while (*string >= '0' && *string <= '9') {
560 number = number * 10 + *string - '0';
561 string++;
562 }
563 npush(number);
564 break;
565
566 case '+':
567 npush(npop() + npop());
568 break;
569
570 case '-':
571 y = npop();
572 x = npop();
573 npush(x - y);
574 break;
575
576 case '*':
577 npush(npop() * npop());
578 break;
579
580 case '/':
581 y = npop();
582 x = npop();
583 npush(y ? (x / y) : 0);
584 break;
585
586 case 'm':
587 y = npop();
588 x = npop();
589 npush(y ? (x % y) : 0);
590 break;
591
592 case 'A':
593 npush(npop() && npop());
594 break;
595
596 case 'O':
597 npush(npop() || npop());
598 break;
599
600 case '&':
601 npush(npop() & npop());
602 break;
603
604 case '|':
605 npush(npop() | npop());
606 break;
607
608 case '^':
609 npush(npop() ^ npop());
610 break;
611
612 case '=':
613 y = npop();
614 x = npop();
615 npush(x == y);
616 break;
617
618 case '<':
619 y = npop();
620 x = npop();
621 npush(x < y);
622 break;
623
624 case '>':
625 y = npop();
626 x = npop();
627 npush(x > y);
628 break;
629
630 case '!':
631 npush(!npop());
632 break;
633
634 case '~':
635 npush(~npop());
636 break;
637
638 case 'i':
639 if (p_is_s[0] == 0)
640 param[0]++;
641 if (p_is_s[1] == 0)
642 param[1]++;
643 break;
644
645 case '?':
646 break;
647
648 case 't':
649 x = npop();
650 if (!x) {
651 /* scan forward for %e or %; at level zero */
652 string++;
653 level = 0;
654 while (*string) {
655 if (*string == '%') {
656 string++;
657 if (*string == '?')
658 level++;
659 else if (*string == ';') {
660 if (level > 0)
661 level--;
662 else
663 break;
664 } else if (*string == 'e' && level == 0)
665 break;
666 }
667
668 if (*string)
669 string++;
670 }
671 }
672 break;
673
674 case 'e':
675 /* scan forward for a %; at level zero */
676 string++;
677 level = 0;
678 while (*string) {
679 if (*string == '%') {
680 string++;
681 if (*string == '?')
682 level++;
683 else if (*string == ';') {
684 if (level > 0)
685 level--;
686 else
687 break;
688 }
689 }
690
691 if (*string)
692 string++;
693 }
694 break;
695
696 case ';':
697 break;
698
699 } /* endswitch (*string) */
700 } else { /* endelse (*string == '%') */
701 save_char(*string);
702 }
703
704 if (*string == '\0')
705 break;
706
707 string++;
708 } /* endwhile (*string) */
709
710 get_space(1);
711 out_buff[out_used] = '\0';
712
713 return (out_buff);
714 }
715
716 char *
grub_tparm(const char * string,...)717 grub_tparm(const char *string,...)
718 {
719 char *result;
720 int *dataptr = (int *) &string;
721
722 dataptr++;
723
724 result = tparam_internal(string, dataptr);
725
726 return result;
727 }
728