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