1/*	$NetBSD: interp_parse.c,v 1.4 2009/07/20 04:59:03 kiyohara Exp $	*/
2
3/*-
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * Jordan K. Hubbard
14 * 29 August 1998
15 *
16 * The meat of the simple parser.
17 */
18
19#include <sys/cdefs.h>
20/* __FBSDID("$FreeBSD: src/sys/boot/common/interp_parse.c,v 1.10 2003/08/25 23:30:41 obrien Exp $"); */
21
22#include <lib/libsa/stand.h>
23#include <lib/libsa/loadfile.h>
24#include <lib/libkern/libkern.h>
25
26#include "bootstrap.h"
27
28static void	 clean(void);
29static int	 insert(int *argcp, char *buf);
30static char	*variable_lookup(char *name);
31
32#define PARSE_BUFSIZE	1024	/* maximum size of one element */
33#define MAXARGS		20	/* maximum number of elements */
34static char		*args[MAXARGS];
35
36/*
37 * parse: accept a string of input and "parse" it for backslash
38 * substitutions and environment variable expansions (${var}),
39 * returning an argc/argv style vector of whitespace separated
40 * arguments.  Returns 0 on success, 1 on failure (ok, ok, so I
41 * wimped-out on the error codes! :).
42 *
43 * Note that the argv array returned must be freed by the caller, but
44 * we own the space allocated for arguments and will free that on next
45 * invocation.  This allows argv consumers to modify the array if
46 * required.
47 *
48 * NB: environment variables that expand to more than one whitespace
49 * separated token will be returned as a single argv[] element, not
50 * split in turn.  Expanded text is also immune to further backslash
51 * elimination or expansion since this is a one-pass, non-recursive
52 * parser.  You didn't specify more than this so if you want more, ask
53 * me. - jkh
54 */
55
56#define PARSE_FAIL(expr) \
57if (expr) { \
58    printf("fail at line %d\n", __LINE__); \
59    clean(); \
60    free(copy); \
61    free(buf); \
62    return 1; \
63}
64
65/* Accept the usual delimiters for a variable, returning counterpart */
66static char
67isdelim(int ch)
68{
69    if (ch == '{')
70	return '}';
71    else if (ch == '(')
72	return ')';
73    return '\0';
74}
75
76static int
77isquote(int ch)
78{
79    return (ch == '\'' || ch == '"');
80}
81
82int
83parse(int *argc, char ***argv, char *str)
84{
85    int ac;
86    char *val, *p, *q, *copy = NULL;
87    size_t i = 0;
88    char token, tmp, quote, *buf;
89    enum { STR, VAR, WHITE } state;
90
91    ac = *argc = 0;
92    quote = 0;
93    if (!str || (p = copy = backslash(str)) == NULL)
94	return 1;
95
96    /* Initialize vector and state */
97    clean();
98    state = STR;
99    buf = (char *)alloc(PARSE_BUFSIZE);
100    token = 0;
101
102    /* And awaaaaaaaaay we go! */
103    while (*p) {
104	switch (state) {
105	case STR:
106	    if ((*p == '\\') && p[1]) {
107		p++;
108		PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
109		buf[i++] = *p++;
110	    } else if (isquote(*p)) {
111		quote = quote ? 0 : *p;
112		++p;
113	    }
114	    else if (isspace(*p) && !quote) {
115		state = WHITE;
116		if (i) {
117		    buf[i] = '\0';
118		    PARSE_FAIL(insert(&ac, buf));
119		    i = 0;
120		}
121		++p;
122	    } else if (*p == '$') {
123		token = isdelim(*(p + 1));
124		if (token)
125		    p += 2;
126		else
127		    ++p;
128		state = VAR;
129	    } else {
130		PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
131		buf[i++] = *p++;
132	    }
133	    break;
134
135	case WHITE:
136	    if (isspace(*p))
137		++p;
138	    else
139		state = STR;
140	    break;
141
142	case VAR:
143	    if (token) {
144		PARSE_FAIL((q = strchr(p, token)) == NULL);
145	    } else {
146		q = p;
147		while (*q && !isspace(*q))
148		    ++q;
149	    }
150	    tmp = *q;
151	    *q = '\0';
152	    if ((val = variable_lookup(p)) != NULL) {
153		size_t len = strlen(val);
154
155		strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1));
156		i += min(len, PARSE_BUFSIZE - 1);
157	    }
158	    *q = tmp;	/* restore value */
159	    p = q + (token ? 1 : 0);
160	    state = STR;
161	    break;
162	}
163    }
164    /* If at end of token, add it */
165    if (i && state == STR) {
166	buf[i] = '\0';
167	PARSE_FAIL(insert(&ac, buf));
168    }
169    args[ac] = NULL;
170    *argc = ac;
171    *argv = (char **)alloc((sizeof(char *) * ac + 1));
172    memcpy(*argv, args, sizeof(char *) * ac + 1);
173    free(buf);
174    free(copy);
175    return 0;
176}
177
178#define MAXARGS	20
179
180/* Clean vector space */
181static void
182clean(void)
183{
184    int		i;
185
186    for (i = 0; i < MAXARGS; i++) {
187	if (args[i] != NULL) {
188	    free(args[i]);
189	    args[i] = NULL;
190	}
191    }
192}
193
194static int
195insert(int *argcp, char *buf)
196{
197    if (*argcp >= MAXARGS)
198	return 1;
199    args[(*argcp)++] = strdup(buf);
200    return 0;
201}
202
203static char *
204variable_lookup(char *name)
205{
206    /* XXX search "special variable" space first? */
207    return (char *)getenv(name);
208}
209