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