199767f8919635c4928607450d9e0abb932109ceToomas Soome/*-
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Redistribution and use in source and binary forms, with or without
199767f8919635c4928607450d9e0abb932109ceToomas Soome * modification, are permitted provided that the following conditions
199767f8919635c4928607450d9e0abb932109ceToomas Soome * are met:
199767f8919635c4928607450d9e0abb932109ceToomas Soome * 1. Redistributions of source code must retain the above copyright
199767f8919635c4928607450d9e0abb932109ceToomas Soome * notice, this list of conditions and the following disclaimer.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
199767f8919635c4928607450d9e0abb932109ceToomas Soome * notice, this list of conditions and the following disclaimer in the
199767f8919635c4928607450d9e0abb932109ceToomas Soome * documentation and/or other materials provided with the distribution.
199767f8919635c4928607450d9e0abb932109ceToomas Soome *
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Jordan K. Hubbard
199767f8919635c4928607450d9e0abb932109ceToomas Soome * 29 August 1998
199767f8919635c4928607450d9e0abb932109ceToomas Soome *
199767f8919635c4928607450d9e0abb932109ceToomas Soome * The meat of the simple parser.
199767f8919635c4928607450d9e0abb932109ceToomas Soome */
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <sys/cdefs.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome__FBSDID("$FreeBSD$");
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <stand.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include <string.h>
199767f8919635c4928607450d9e0abb932109ceToomas Soome#include "bootstrap.h"
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void clean(void);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int insert(int *argcp, char *buf);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *variable_lookup(char *name);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define PARSE_BUFSIZE 1024 /* maximum size of one element */
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define MAXARGS 20 /* maximum number of elements */
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *args[MAXARGS];
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome/*
199767f8919635c4928607450d9e0abb932109ceToomas Soome * parse: accept a string of input and "parse" it for backslash
199767f8919635c4928607450d9e0abb932109ceToomas Soome * substitutions and environment variable expansions (${var}),
199767f8919635c4928607450d9e0abb932109ceToomas Soome * returning an argc/argv style vector of whitespace separated
199767f8919635c4928607450d9e0abb932109ceToomas Soome * arguments. Returns 0 on success, 1 on failure (ok, ok, so I
199767f8919635c4928607450d9e0abb932109ceToomas Soome * wimped-out on the error codes! :).
199767f8919635c4928607450d9e0abb932109ceToomas Soome *
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Note that the argv array returned must be freed by the caller, but
199767f8919635c4928607450d9e0abb932109ceToomas Soome * we own the space allocated for arguments and will free that on next
199767f8919635c4928607450d9e0abb932109ceToomas Soome * invocation. This allows argv consumers to modify the array if
199767f8919635c4928607450d9e0abb932109ceToomas Soome * required.
199767f8919635c4928607450d9e0abb932109ceToomas Soome *
199767f8919635c4928607450d9e0abb932109ceToomas Soome * NB: environment variables that expand to more than one whitespace
199767f8919635c4928607450d9e0abb932109ceToomas Soome * separated token will be returned as a single argv[] element, not
199767f8919635c4928607450d9e0abb932109ceToomas Soome * split in turn. Expanded text is also immune to further backslash
199767f8919635c4928607450d9e0abb932109ceToomas Soome * elimination or expansion since this is a one-pass, non-recursive
199767f8919635c4928607450d9e0abb932109ceToomas Soome * parser. You didn't specify more than this so if you want more, ask
199767f8919635c4928607450d9e0abb932109ceToomas Soome * me. - jkh
199767f8919635c4928607450d9e0abb932109ceToomas Soome */
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define PARSE_FAIL(expr) \
199767f8919635c4928607450d9e0abb932109ceToomas Soomeif (expr) { \
199767f8919635c4928607450d9e0abb932109ceToomas Soome printf("fail at line %d\n", __LINE__); \
199767f8919635c4928607450d9e0abb932109ceToomas Soome clean(); \
199767f8919635c4928607450d9e0abb932109ceToomas Soome free(copy); \
199767f8919635c4928607450d9e0abb932109ceToomas Soome free(buf); \
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1; \
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Accept the usual delimiters for a variable, returning counterpart */
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char
199767f8919635c4928607450d9e0abb932109ceToomas Soomeisdelim(int ch)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (ch == '{')
199767f8919635c4928607450d9e0abb932109ceToomas Soome return '}';
199767f8919635c4928607450d9e0abb932109ceToomas Soome else if (ch == '(')
199767f8919635c4928607450d9e0abb932109ceToomas Soome return ')';
199767f8919635c4928607450d9e0abb932109ceToomas Soome return '\0';
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int
199767f8919635c4928607450d9e0abb932109ceToomas Soomeisquote(int ch)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return (ch == '\'');
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int
199767f8919635c4928607450d9e0abb932109ceToomas Soomeisdquote(int ch)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome return (ch == '"');
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomeint
199767f8919635c4928607450d9e0abb932109ceToomas Soomeparse(int *argc, char ***argv, char *str)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int ac;
199767f8919635c4928607450d9e0abb932109ceToomas Soome char *val, *p, *q, *copy = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome size_t i = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome char token, tmp, quote, dquote, *buf;
199767f8919635c4928607450d9e0abb932109ceToomas Soome enum { STR, VAR, WHITE } state;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome ac = *argc = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome dquote = quote = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (!str || (p = copy = backslash(str)) == NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Initialize vector and state */
199767f8919635c4928607450d9e0abb932109ceToomas Soome clean();
199767f8919635c4928607450d9e0abb932109ceToomas Soome state = STR;
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf = (char *)malloc(PARSE_BUFSIZE);
199767f8919635c4928607450d9e0abb932109ceToomas Soome token = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* And awaaaaaaaaay we go! */
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (*p) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome switch (state) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome case STR:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((*p == '\\') && p[1]) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome p++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i++] = *p++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else if (isquote(*p)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome quote = quote ? 0 : *p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (dquote) { /* keep quote */
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i++] = *p++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else if (isdquote(*p)) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome dquote = dquote ? 0 : *p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (quote) { /* keep dquote */
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i++] = *p++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else if (isspace(*p) && !quote && !dquote) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome state = WHITE;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (i) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i] = '\0';
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(insert(&ac, buf));
199767f8919635c4928607450d9e0abb932109ceToomas Soome i = 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else if (*p == '$' && !quote) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome token = isdelim(*(p + 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (token)
199767f8919635c4928607450d9e0abb932109ceToomas Soome p += 2;
199767f8919635c4928607450d9e0abb932109ceToomas Soome else
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome state = VAR;
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(i == (PARSE_BUFSIZE - 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i++] = *p++;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome case WHITE:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (isspace(*p))
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome else
199767f8919635c4928607450d9e0abb932109ceToomas Soome state = STR;
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome case VAR:
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (token) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL((q = strchr(p, token)) == NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else {
199767f8919635c4928607450d9e0abb932109ceToomas Soome q = p;
199767f8919635c4928607450d9e0abb932109ceToomas Soome while (*q && !isspace(*q))
199767f8919635c4928607450d9e0abb932109ceToomas Soome ++q;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome tmp = *q;
199767f8919635c4928607450d9e0abb932109ceToomas Soome *q = '\0';
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((val = variable_lookup(p)) != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome size_t len = strlen(val);
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome i += min(len, PARSE_BUFSIZE - 1);
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome *q = tmp; /* restore value */
199767f8919635c4928607450d9e0abb932109ceToomas Soome p = q + (token ? 1 : 0);
199767f8919635c4928607450d9e0abb932109ceToomas Soome state = STR;
199767f8919635c4928607450d9e0abb932109ceToomas Soome break;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* missing terminating ' or " */
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(quote || dquote);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* If at end of token, add it */
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (i && state == STR) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome buf[i] = '\0';
199767f8919635c4928607450d9e0abb932109ceToomas Soome PARSE_FAIL(insert(&ac, buf));
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome args[ac] = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome *argc = ac;
199767f8919635c4928607450d9e0abb932109ceToomas Soome *argv = (char **)malloc((sizeof(char *) * ac + 1));
199767f8919635c4928607450d9e0abb932109ceToomas Soome bcopy(args, *argv, sizeof(char *) * ac + 1);
199767f8919635c4928607450d9e0abb932109ceToomas Soome free(buf);
199767f8919635c4928607450d9e0abb932109ceToomas Soome free(copy);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome#define MAXARGS 20
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* Clean vector space */
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void
199767f8919635c4928607450d9e0abb932109ceToomas Soomeclean(void)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome int i;
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (i = 0; i < MAXARGS; i++) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (args[i] != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome free(args[i]);
199767f8919635c4928607450d9e0abb932109ceToomas Soome args[i] = NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome }
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int
199767f8919635c4928607450d9e0abb932109ceToomas Soomeinsert(int *argcp, char *buf)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (*argcp >= MAXARGS)
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 1;
199767f8919635c4928607450d9e0abb932109ceToomas Soome args[(*argcp)++] = strdup(buf);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return 0;
199767f8919635c4928607450d9e0abb932109ceToomas Soome}
199767f8919635c4928607450d9e0abb932109ceToomas Soome
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomevariable_lookup(char *name)
199767f8919635c4928607450d9e0abb932109ceToomas Soome{
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* XXX search "special variable" space first? */
199767f8919635c4928607450d9e0abb932109ceToomas Soome return (char *)getenv(name);
199767f8919635c4928607450d9e0abb932109ceToomas Soome}