Home | History | Annotate | Download | only in stage2
      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 (at) reinform.de> 2002
     36  **********************************************************************/
     37 
     38 /****************************************************************************
     39  *  Author: Zeyd M. Ben-Halim <zmbenhal (at) netcom.com> 1992,1995               *
     40  *     and: Eric S. Raymond <esr (at) 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
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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 *
    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 *
    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