• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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