From 798de5071d2a775af8931e99560a0eb9f72dfec3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Jul 2007 18:01:42 +0200 Subject: svn_rev_307 some main.c cleanup: - move the parser used when hush is disabled to parser.c - move readline related functions to readline.c (with cmdline editing) and readline_simple.c (no cmdline editing) --- common/parser.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 common/parser.c (limited to 'common/parser.c') diff --git a/common/parser.c b/common/parser.c new file mode 100644 index 0000000000..5716a13212 --- /dev/null +++ b/common/parser.c @@ -0,0 +1,302 @@ +#include +#include + +int parse_line (char *line, char *argv[]) +{ + int nargs = 0; + +#ifdef DEBUG_PARSER + printf ("parse_line: \"%s\"\n", line); +#endif + while (nargs < CONFIG_MAXARGS) { + + /* skip any white space */ + while ((*line == ' ') || (*line == '\t')) { + ++line; + } + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); + } + + argv[nargs++] = line; /* begin of argument string */ + + /* find end of string */ + while (*line && (*line != ' ') && (*line != '\t')) { + ++line; + } + + if (*line == '\0') { /* end of line, no more args */ + argv[nargs] = NULL; +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); + } + + *line++ = '\0'; /* terminate current arg */ + } + + printf ("** Too many args (max. %d) **\n", CONFIG_MAXARGS); + +#ifdef DEBUG_PARSER + printf ("parse_line: nargs=%d\n", nargs); +#endif + return (nargs); +} + +static void process_macros (const char *input, char *output) +{ + char c, prev; + const char *varname_start = NULL; + int inputcnt = strlen (input); + int outputcnt = CONFIG_CBSIZE; + int state = 0; /* 0 = waiting for '$' */ + + /* 1 = waiting for '(' or '{' */ + /* 2 = waiting for ')' or '}' */ + /* 3 = waiting for ''' */ +#ifdef DEBUG_PARSER + char *output_start = output; + + printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input), + input); +#endif + + prev = '\0'; /* previous character */ + + while (inputcnt && outputcnt) { + c = *input++; + inputcnt--; + + if (state != 3) { + /* remove one level of escape characters */ + if ((c == '\\') && (prev != '\\')) { + if (inputcnt-- == 0) + break; + prev = c; + c = *input++; + } + } + + switch (state) { + case 0: /* Waiting for (unescaped) $ */ + if ((c == '\'') && (prev != '\\')) { + state = 3; + break; + } + if ((c == '$') && (prev != '\\')) { + state++; + } else { + *(output++) = c; + outputcnt--; + } + break; + case 1: /* Waiting for ( */ + if (c == '(' || c == '{') { + state++; + varname_start = input; + } else { + state = 0; + *(output++) = '$'; + outputcnt--; + + if (outputcnt) { + *(output++) = c; + outputcnt--; + } + } + break; + case 2: /* Waiting for ) */ + if (c == ')' || c == '}') { + int i; + char envname[CONFIG_CBSIZE], *envval; + int envcnt = input - varname_start - 1; /* Varname # of chars */ + + /* Get the varname */ + for (i = 0; i < envcnt; i++) { + envname[i] = varname_start[i]; + } + envname[i] = 0; + + /* Get its value */ + envval = getenv (envname); + + /* Copy into the line if it exists */ + if (envval != NULL) + while ((*envval) && outputcnt) { + *(output++) = *(envval++); + outputcnt--; + } + /* Look for another '$' */ + state = 0; + } + break; + case 3: /* Waiting for ' */ + if ((c == '\'') && (prev != '\\')) { + state = 0; + } else { + *(output++) = c; + outputcnt--; + } + break; + } + prev = c; + } + + if (outputcnt) + *output = 0; + +#ifdef DEBUG_PARSER + printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", + strlen (output_start), output_start); +#endif +} + +/**************************************************************************** + * returns: + * 1 - command executed, repeatable + * 0 - command executed but not repeatable, interrupted commands are + * always considered not repeatable + * -1 - not executed (unrecognized, bootd recursion or too many args) + * (If cmd is NULL or "" or longer than CONFIG_CBSIZE-1 it is + * considered unrecognized) + * + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ + +int run_command (const char *cmd, int flag) +{ + cmd_tbl_t *cmdtp; + char cmdbuf[CONFIG_CBSIZE]; /* working copy of cmd */ + char *token; /* start of token in cmdbuf */ + char *sep; /* end of token (separator) in cmdbuf */ + char finaltoken[CONFIG_CBSIZE]; + char *str = cmdbuf; + char *argv[CONFIG_MAXARGS + 1]; /* NULL terminated */ + int argc, inquotes; + int repeatable = 1; + int rc = 0; + +#ifdef DEBUG_PARSER + printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); + puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ + puts ("\"\n"); +#endif + + clear_ctrlc(); /* forget any previous Control C */ + + if (!cmd || !*cmd) { + return -1; /* empty command */ + } + + if (strlen(cmd) >= CONFIG_CBSIZE) { + puts ("## Command too long!\n"); + return -1; + } + + strcpy (cmdbuf, cmd); + + /* Process separators and check for invalid + * repeatable commands + */ + +#ifdef DEBUG_PARSER + printf ("[PROCESS_SEPARATORS] %s\n", cmd); +#endif + while (*str) { + + /* + * Find separator, or string end + * Allow simple escape of ';' by writing "\;" + */ + for (inquotes = 0, sep = str; *sep; sep++) { + if ((*sep=='\'') && + (*(sep-1) != '\\')) + inquotes=!inquotes; + + if (!inquotes && + (*sep == ';') && /* separator */ + ( sep != str) && /* past string start */ + (*(sep-1) != '\\')) /* and NOT escaped */ + break; + } + + /* + * Limit the token to data between separators + */ + token = str; + if (*sep) { + str = sep + 1; /* start of command for next pass */ + *sep = '\0'; + } + else + str = sep; /* no more commands for next pass */ +#ifdef DEBUG_PARSER + printf ("token: \"%s\"\n", token); +#endif + + /* find macros in this token and replace them */ + process_macros (token, finaltoken); + + /* Extract arguments */ + if ((argc = parse_line (finaltoken, argv)) == 0) { + rc = -1; /* no command at all */ + continue; + } + + /* Look up command in command table */ + if ((cmdtp = find_cmd(argv[0])) == NULL) { + printf ("Unknown command '%s' - try 'help'\n", argv[0]); + rc = -1; /* give up after bad command */ + continue; + } + + /* found - check max args */ + if (argc > cmdtp->maxargs) { + printf ("Usage:\n%s\n", cmdtp->usage); + rc = -1; + continue; + } + +#if (CONFIG_COMMANDS & CFG_CMD_BOOTD) + /* avoid "bootd" recursion */ + if (cmdtp->cmd == do_bootd) { +#ifdef DEBUG_PARSER + printf ("[%s]\n", finaltoken); +#endif + if (flag & CMD_FLAG_BOOTD) { + puts ("'bootd' recursion detected\n"); + rc = -1; + continue; + } else { + flag |= CMD_FLAG_BOOTD; + } + } +#endif /* CFG_CMD_BOOTD */ + + /* OK - call function to do the command */ + if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { + rc = -1; + } + + repeatable &= cmdtp->repeatable; + + /* Did the user stop this? */ + if (had_ctrlc ()) + return 0; /* if stopped then not repeatable */ + } + + return rc ? rc : repeatable; +} + +/****************************************************************************/ -- cgit v1.2.3