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 */
55typedef 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
131typedef struct {
132    union {
133	unsigned int num;
134	char *str;
135    } data;
136    grub_bool num_type;
137} stack_frame;
138
139static stack_frame stack[STACKSIZE];
140static int stack_ptr;
141
142static char out_buff[256];
143static int out_size = 256;
144static int out_used;
145
146static inline void
147get_space(int need)
148{
149    need += out_used;
150    if (need > out_size) {
151	// FIX ME! buffer full, what now?
152	;
153    }
154}
155
156static inline void
157save_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
169static inline void
170save_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
181static inline void
182save_char(int c)
183{
184    if (c == 0)
185	c = 0200;
186    get_space(1);
187    out_buff[out_used++] = c;
188}
189
190static inline void
191npush(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
200static inline int
201npop(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
212static inline void
213spush(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
222static inline char *
223spop(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
235static inline const char *
236parse_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
323static inline char *
324tparam_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
716char *
717grub_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