/*-1* Redistribution and use in source and binary forms, with or without2* modification, are permitted provided that the following conditions3* are met:4* 1. Redistributions of source code must retain the above copyright5* notice, this list of conditions and the following disclaimer.6* 2. Redistributions in binary form must reproduce the above copyright7* notice, this list of conditions and the following disclaimer in the8* documentation and/or other materials provided with the distribution.9*10* Jordan K. Hubbard11* 29 August 199812*13* The meat of the simple parser.14*/1516#include <stand.h>17#include <string.h>18#include "bootstrap.h"1920static void clean(void);21static int insert(int *argcp, char *buf);22static char *variable_lookup(char *name);2324#define PARSE_BUFSIZE 1024 /* maximum size of one element */25#define MAXARGS 20 /* maximum number of elements */26static char *args[MAXARGS];2728/*29* parse: accept a string of input and "parse" it for backslash30* substitutions and environment variable expansions (${var}),31* returning an argc/argv style vector of whitespace separated32* arguments. Returns 0 on success, 1 on failure (ok, ok, so I33* wimped-out on the error codes! :).34*35* Note that the argv array returned must be freed by the caller, but36* we own the space allocated for arguments and will free that on next37* invocation. This allows argv consumers to modify the array if38* required.39*40* NB: environment variables that expand to more than one whitespace41* separated token will be returned as a single argv[] element, not42* split in turn. Expanded text is also immune to further backslash43* elimination or expansion since this is a one-pass, non-recursive44* parser. You didn't specify more than this so if you want more, ask45* me. - jkh46*/4748#define PARSE_FAIL(expr) \49if (expr) { \50printf("fail at line %d\n", __LINE__); \51clean(); \52free(copy); \53free(buf); \54return 1; \55}5657/* Accept the usual delimiters for a variable, returning counterpart */58static char59isdelim(int ch)60{6162if (ch == '{')63return '}';64else if (ch == '(')65return ')';66return '\0';67}6869static int70isquote(int ch)71{7273return (ch == '\'');74}7576static int77isdquote(int ch)78{7980return (ch == '"');81}8283int84parse(int *argc, char ***argv, const char *str)85{86int ac;87char *val, *p, *q, *copy = NULL;88size_t i = 0;89char token, tmp, quote, dquote, *buf;90enum { STR, VAR, WHITE } state;9192ac = *argc = 0;93dquote = quote = 0;94if (!str || (p = copy = backslash(str)) == NULL)95return 1;9697/* Initialize vector and state */98clean();99state = STR;100buf = (char *)malloc(PARSE_BUFSIZE);101token = 0;102103/* And awaaaaaaaaay we go! */104while (*p) {105switch (state) {106case STR:107if ((*p == '\\') && p[1]) {108p++;109PARSE_FAIL(i == (PARSE_BUFSIZE - 1));110buf[i++] = *p++;111} else if (isquote(*p)) {112quote = quote ? 0 : *p;113if (dquote) { /* keep quote */114PARSE_FAIL(i == (PARSE_BUFSIZE - 1));115buf[i++] = *p++;116} else117++p;118} else if (isdquote(*p)) {119dquote = dquote ? 0 : *p;120if (quote) { /* keep dquote */121PARSE_FAIL(i == (PARSE_BUFSIZE - 1));122buf[i++] = *p++;123} else124++p;125} else if (isspace(*p) && !quote && !dquote) {126state = WHITE;127if (i) {128buf[i] = '\0';129PARSE_FAIL(insert(&ac, buf));130i = 0;131}132++p;133} else if (*p == '$' && !quote) {134token = isdelim(*(p + 1));135if (token)136p += 2;137else138++p;139state = VAR;140} else {141PARSE_FAIL(i == (PARSE_BUFSIZE - 1));142buf[i++] = *p++;143}144break;145146case WHITE:147if (isspace(*p))148++p;149else150state = STR;151break;152153case VAR:154if (token) {155PARSE_FAIL((q = strchr(p, token)) == NULL);156} else {157q = p;158while (*q && !isspace(*q))159++q;160}161tmp = *q;162*q = '\0';163if ((val = variable_lookup(p)) != NULL) {164size_t len = strlen(val);165166strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1));167i += min(len, PARSE_BUFSIZE - 1);168}169*q = tmp; /* restore value */170p = q + (token ? 1 : 0);171state = STR;172break;173}174}175/* missing terminating ' or " */176PARSE_FAIL(quote || dquote);177/* If at end of token, add it */178if (i && state == STR) {179buf[i] = '\0';180PARSE_FAIL(insert(&ac, buf));181}182args[ac] = NULL;183*argc = ac;184*argv = (char **)malloc((sizeof(char *) * ac + 1));185bcopy(args, *argv, sizeof(char *) * ac + 1);186free(buf);187free(copy);188return 0;189}190191#define MAXARGS 20192193/* Clean vector space */194static void195clean(void)196{197int i;198199for (i = 0; i < MAXARGS; i++) {200if (args[i] != NULL) {201free(args[i]);202args[i] = NULL;203}204}205}206207static int208insert(int *argcp, char *buf)209{210211if (*argcp >= MAXARGS)212return 1;213args[(*argcp)++] = strdup(buf);214return 0;215}216217static char *218variable_lookup(char *name)219{220221/* XXX search "special variable" space first? */222return (char *)getenv(name);223}224225226