summaryrefslogtreecommitdiffstats
path: root/common/parser.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2007-07-05 18:01:42 +0200
committerSascha Hauer <sha@octopus.labnet.pengutronix.de>2007-07-05 18:01:42 +0200
commit798de5071d2a775af8931e99560a0eb9f72dfec3 (patch)
tree4a6b10d60d6f612a7589ba301a5fd2a47fb38b25 /common/parser.c
parenta26b7ce1209358ba6580382d1e0f8ebaa31b42d1 (diff)
downloadbarebox-798de5071d2a775af8931e99560a0eb9f72dfec3.tar.gz
barebox-798de5071d2a775af8931e99560a0eb9f72dfec3.tar.xz
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)
Diffstat (limited to 'common/parser.c')
-rw-r--r--common/parser.c302
1 files changed, 302 insertions, 0 deletions
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 <common.h>
+#include <command.h>
+
+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;
+}
+
+/****************************************************************************/