2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions
5 * 1. Redistributions of source code must retain the above copyright
6 * notice, this list of conditions and the following disclaimer.
7 * 2. Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
14 * The meat of the simple parser.
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
22 #include "bootstrap.h"
24 static void clean(void);
25 static int insert(int *argcp
, char *buf
);
26 static char *variable_lookup(char *name
);
28 #define PARSE_BUFSIZE 1024 /* maximum size of one element */
29 #define MAXARGS 20 /* maximum number of elements */
30 static char *args
[MAXARGS
];
33 * parse: accept a string of input and "parse" it for backslash
34 * substitutions and environment variable expansions (${var}),
35 * returning an argc/argv style vector of whitespace separated
36 * arguments. Returns 0 on success, 1 on failure (ok, ok, so I
37 * wimped-out on the error codes! :).
39 * Note that the argv array returned must be freed by the caller, but
40 * we own the space allocated for arguments and will free that on next
41 * invocation. This allows argv consumers to modify the array if
44 * NB: environment variables that expand to more than one whitespace
45 * separated token will be returned as a single argv[] element, not
46 * split in turn. Expanded text is also immune to further backslash
47 * elimination or expansion since this is a one-pass, non-recursive
48 * parser. You didn't specify more than this so if you want more, ask
52 #define PARSE_FAIL(expr) \
54 printf("fail at line %d\n", __LINE__); \
61 /* Accept the usual delimiters for a variable, returning counterpart */
85 parse(int *argc
, char ***argv
, char *str
)
88 char *val
, *p
, *q
, *copy
= NULL
;
90 char token
, tmp
, quote
, dquote
, *buf
;
91 enum { STR
, VAR
, WHITE
} state
;
95 if (!str
|| (p
= copy
= backslash(str
)) == NULL
)
98 /* Initialize vector and state */
101 buf
= (char *)malloc(PARSE_BUFSIZE
);
104 /* And awaaaaaaaaay we go! */
108 if ((*p
== '\\') && p
[1]) {
110 PARSE_FAIL(i
== (PARSE_BUFSIZE
- 1));
112 } else if (isquote(*p
)) {
113 quote
= quote
? 0 : *p
;
114 if (dquote
) { /* keep quote */
115 PARSE_FAIL(i
== (PARSE_BUFSIZE
- 1));
120 } else if (isdquote(*p
)) {
121 dquote
= dquote
? 0 : *p
;
122 if (quote
) { /* keep dquote */
123 PARSE_FAIL(i
== (PARSE_BUFSIZE
- 1));
128 } else if (isspace(*p
) && !quote
&& !dquote
) {
132 PARSE_FAIL(insert(&ac
, buf
));
136 } else if (*p
== '$' && !quote
) {
137 token
= isdelim(*(p
+ 1));
144 PARSE_FAIL(i
== (PARSE_BUFSIZE
- 1));
158 PARSE_FAIL((q
= strchr(p
, token
)) == NULL
);
161 while (*q
&& !isspace(*q
))
166 if ((val
= variable_lookup(p
)) != NULL
) {
167 size_t len
= strlen(val
);
169 strncpy(buf
+ i
, val
, PARSE_BUFSIZE
- (i
+ 1));
170 i
+= min(len
, PARSE_BUFSIZE
- 1);
172 *q
= tmp
; /* restore value */
173 p
= q
+ (token
? 1 : 0);
178 /* missing terminating ' or " */
179 PARSE_FAIL(quote
|| dquote
);
180 /* If at end of token, add it */
181 if (i
&& state
== STR
) {
183 PARSE_FAIL(insert(&ac
, buf
));
187 *argv
= (char **)malloc((sizeof(char *) * ac
+ 1));
188 bcopy(args
, *argv
, sizeof(char *) * ac
+ 1);
196 /* Clean vector space */
202 for (i
= 0; i
< MAXARGS
; i
++) {
203 if (args
[i
] != NULL
) {
211 insert(int *argcp
, char *buf
)
213 if (*argcp
>= MAXARGS
)
215 args
[(*argcp
)++] = strdup(buf
);
220 variable_lookup(char *name
)
222 /* XXX search "special variable" space first? */
223 return (char *)getenv(name
);