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(5)):
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