diff options
author | msmith <msmith@FreeBSD.org> | 1998-09-01 00:41:24 +0000 |
---|---|---|
committer | msmith <msmith@FreeBSD.org> | 1998-09-01 00:41:24 +0000 |
commit | a43b5b132e554f3e04a3559c49750aef07be090a (patch) | |
tree | 835b8093ffcc83efebe6488fc290c1cff86b7a81 | |
parent | f038ce5877c579f9724ba83bb8f86cc3e22ad294 (diff) | |
download | FreeBSD-src-a43b5b132e554f3e04a3559c49750aef07be090a.zip FreeBSD-src-a43b5b132e554f3e04a3559c49750aef07be090a.tar.gz |
New commandline/script parser, supports backslash quoting and environment
variable substitution.
Submitted by: Jordan Hubbard <jkh@freebsd.org>
-rw-r--r-- | sys/boot/common/Makefile.inc | 6 | ||||
-rw-r--r-- | sys/boot/common/interp.c | 55 | ||||
-rw-r--r-- | sys/boot/common/interp_backslash.c | 157 | ||||
-rw-r--r-- | sys/boot/common/interp_parse.c | 190 |
4 files changed, 365 insertions, 43 deletions
diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc index aa33c80..ea7679d 100644 --- a/sys/boot/common/Makefile.inc +++ b/sys/boot/common/Makefile.inc @@ -1,4 +1,4 @@ -# $Id: Makefile.inc,v 1.1.1.1 1998/08/21 03:17:41 msmith Exp $ +# $Id: Makefile.inc,v 1.2 1998/08/31 21:10:42 msmith Exp $ -SRCS+= boot.c commands.c console.c devopen.c interp.c load_aout.c -SRCS+= ls.c misc.c module.c panic.c +SRCS+= boot.c commands.c console.c devopen.c interp.c interp_backslash.c +SRCS+= interp_parse.c load_aout.c ls.c misc.c module.c panic.c diff --git a/sys/boot/common/interp.c b/sys/boot/common/interp.c index 2bd75f4..a0162ba 100644 --- a/sys/boot/common/interp.c +++ b/sys/boot/common/interp.c @@ -23,10 +23,10 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: interp.c,v 1.1.1.1 1998/08/21 03:17:41 msmith Exp $ */ /* - * Simple commandline interpreter. + * Simple commandline interpreter, toplevel and misc. * * XXX may be obsoleted by BootFORTH */ @@ -37,40 +37,9 @@ #define MAXARGS 20 /* maximum number of arguments allowed */ -static int parse(char *buf, int *argcp, char **argvp[]); +extern int parse(int *argc, char ***argv, char *str); /* interp_parse.c */ + static void prompt(void); -/* - * Parse the supplied text into argv/argc form. - * XXX should perhaps learn about quotes, etc? - * XXX can also do alias expansion, variable substitution, etc. here - */ -static int -parse(char *buf, int *argcp, char **argvp[]) -{ - static int argc; - static char *argv[MAXARGS], *cp; - - argc = 0; - cp = buf; - while ((*cp != 0) && (argc < MAXARGS)) { - if (isspace(*cp)) { - *(cp++) = 0; - } else { - argv[argc++] = cp++; - while ((*cp != 0) && !isspace(*cp)) - cp++; - } - } - argv[argc] = NULL; - /* command too complex */ - if (argc >= MAXARGS) { - printf("too many arguments\n"); - return(1); - } - *argcp = argc; - *argvp = argv; - return(0); -} /* * Perform the command @@ -118,9 +87,13 @@ interact(void) input[0] = '\0'; prompt(); ngets(input, sizeof(input)); - if (!parse(input, &argc, &argv) && - (perform(argc, argv) != 0)) + if (!parse(&argc, &argv, input)) { + if (perform(argc, argv)) printf("%s: %s\n", argv[0], command_errmsg); + free(argv); + } else { + printf("parse error\n"); + } } } @@ -164,10 +137,12 @@ source(char *filename) printf("%s\n", input); } - if (!parse(cp, &argc, &argv) && - (argc > 0) && - (perform(argc, argv) != 0)) + if (!parse(&argc, &argv, cp)) { + if ((argc > 0) && + (perform(argc, argv) != 0)) printf("%s: %s\n", argv[0], command_errmsg); + free(argv); + } } close(fd); } diff --git a/sys/boot/common/interp_backslash.c b/sys/boot/common/interp_backslash.c new file mode 100644 index 0000000..cbabb7c --- /dev/null +++ b/sys/boot/common/interp_backslash.c @@ -0,0 +1,157 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 29 August 1998 + * + * $Id$ + * + * Routine for doing backslash elimination. + */ + +#include <stand.h> +#include <string.h> + +#define DIGIT(x) (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') + +/* + * backslash: Return malloc'd copy of str with all standard "backslash + * processing" done on it. Original can be free'd if desired. + */ +char * +backslash(char *str) +{ + /* + * Remove backslashes from the strings. Turn \040 etc. into a single + * character (we allow eight bit values). Currently NUL is not + * allowed. + * + * Turn "\n" and "\t" into '\n' and '\t' characters. Etc. + * + */ + char *new_str; + int seenbs = 0; + int i = 0; + + if ((new_str = strdup(str)) == NULL) + return NULL; + + while (*str) { + if (seenbs) { + seenbs = 0; + switch (*str) { + case '\\': + new_str[i++] = '\\'; + str++; + break; + + case 'b': + new_str[i++] = '\b'; + str++; + break; + + case 'f': + new_str[i++] = '\f'; + str++; + break; + + case 'r': + new_str[i++] = '\r'; + str++; + break; + + case 'n': + new_str[i++] = '\n'; + str++; + break; + + case 's': + new_str[i++] = ' '; + str++; + break; + + case 't': + new_str[i++] = '\t'; + str++; + break; + + case 'v': + new_str[i++] = '\13'; + str++; + break; + + case 'z': + str++; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + char val; + + /* Three digit octal constant? */ + if (*str >= '0' && *str <= '3' && + *(str + 1) >= '0' && *(str + 1) <= '7' && + *(str + 2) >= '0' && *(str + 2) <= '7') { + + val = (DIGIT(*str) << 6) + (DIGIT(*(str + 1)) << 3) + + DIGIT(*(str + 2)); + + /* Allow null value if user really wants to shoot + at feet, but beware! */ + new_str[i++] = val; + str += 3; + break; + } + + /* One or two digit hex constant? + * If two are there they will both be taken. + * Use \z to split them up if this is not wanted. + */ + if (*str == '0' && + (*(str + 1) == 'x' || *(str + 1) == 'X') && + isxdigit(*(str + 2))) { + val = DIGIT(*(str + 2)); + if (isxdigit(*(str + 3))) { + val = (val << 4) + DIGIT(*(str + 3)); + str += 4; + } + else + str += 3; + /* Yep, allow null value here too */ + new_str[i++] = val; + break; + } + } + break; + + default: + new_str[i++] = *str++; + break; + } + } + else { + if (*str == '\\') { + seenbs = 1; + str++; + } + else + new_str[i++] = *str++; + } + } + + if (seenbs) { + /* + * The final character was a '\'. Put it in as a single backslash. + */ + new_str[i++] = '\\'; + } + new_str[i] = '\0'; + return new_str; +} diff --git a/sys/boot/common/interp_parse.c b/sys/boot/common/interp_parse.c new file mode 100644 index 0000000..ff5c274 --- /dev/null +++ b/sys/boot/common/interp_parse.c @@ -0,0 +1,190 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Jordan K. Hubbard + * 29 August 1998 + * + * $Id$ + * + * The meat of the simple parser. + */ + +#include <stand.h> +#include <string.h> + +/* Forward decls */ +extern char *backslash(char *str); + +static void init(int *argcp, char ***argvp); +static void clean(void); +static int insert(int *argcp, char *buf); +static char *variable_lookup(char *name); + +#define PARSE_BUFSIZE 1024 /* maximum size of one element */ +#define MAXARGS 20 /* maximum number of elements */ +static char *args[MAXARGS]; + +/* + * parse: accept a string of input and "parse" it for backslash + * substitutions and environment variable expansions (${var}), + * returning an argc/argv style vector of whitespace separated + * arguments. Returns 0 on success, 1 on failure (ok, ok, so I + * wimped-out on the error codes! :). + * + * Note that the argv array returned must be freed by the caller, but + * we own the space allocated for arguments and will free that on next + * invocation. This allows argv consumers to modify the array if + * required. + * + * NB: environment variables that expand to more than one whitespace + * separated token will be returned as a single argv[] element, not + * split in turn. Expanded text is also immune to further backslash + * elimination or expansion since this is a one-pass, non-recursive + * parser. You didn't specify more than this so if you want more, ask + * me. - jkh + */ + +#define PARSE_FAIL(expr) \ +if (expr) { \ + printf("fail at line %d\n", __LINE__); \ + clean(); \ + free(copy); \ + free(buf); \ + return 1; \ +} + +/* Accept the usual delimiters for a variable, returning counterpart */ +static char +isdelim(char ch) +{ + if (ch == '{') + return '}'; + else if (ch == '(') + return ')'; + return '\0'; +} + +int +parse(int *argc, char ***argv, char *str) +{ + int ac; + char *val, *p, *q, *copy = NULL; + int i = 0; + char token, tmp, *buf; + enum { STR, VAR, WHITE } state; + + ac = *argc = 0; + if (!str || (p = copy = backslash(str)) == NULL) + return 1; + + /* Initialize vector and state */ + clean(); + state = STR; + buf = malloc(PARSE_BUFSIZE); + token = 0; + + /* And awaaaaaaaaay we go! */ + while (*p) { + switch (state) { + case STR: + if (isspace(*p)) { + state = WHITE; + if (i) { + buf[i] = '\0'; + PARSE_FAIL(insert(&ac, buf)); + i = 0; + } + ++p; + } else if (*p == '$') { + token = isdelim(*(p + 1)); + if (token) + p += 2; + else + ++p; + state = VAR; + } else { + PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); + buf[i++] = *p++; + } + break; + + case WHITE: + if (isspace(*p)) + ++p; + else + state = STR; + break; + + case VAR: + if (token) { + PARSE_FAIL((q = index(p, token)) == NULL); + } else { + q = p; + while (*q && !isspace(*q)) + ++q; + } + tmp = *q; + *q = '\0'; + if ((val = variable_lookup(p)) != NULL) { + int len = strlen(val); + + strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1)); + i += min(len, sizeof(buf) - 1); + } + *q = tmp; /* restore value */ + p = q + (token ? 1 : 0); + state = STR; + break; + } + } + /* If at end of token, add it */ + if (i && state == STR) { + buf[i] = '\0'; + PARSE_FAIL(insert(&ac, buf)); + } + args[ac] = NULL; + *argc = ac; + *argv = malloc((sizeof(char *) * ac + 1)); + bcopy(args, *argv, sizeof(char *) * ac + 1); + free(copy); + return 0; +} + +#define MAXARGS 20 + +/* Clean vector space */ +static void +clean(void) +{ + int i; + + for (i = 0; i < MAXARGS; i++) { + if (args[i] != NULL) { + free(args[i]); + args[i] = NULL; + } + } +} + +static int +insert(int *argcp, char *buf) +{ + if (*argcp >= MAXARGS) + return 1; + args[(*argcp)++] = strdup(buf); + return 0; +} + +static char * +variable_lookup(char *name) +{ + /* XXX search "special variable" space first? */ + return (char *)getenv(name); +} |