]> cloudbase.mooo.com Git - z180-stamp.git/blobdiff - avr/cli.c
cli.c bugfix: Comment only lines are not an error.
[z180-stamp.git] / avr / cli.c
index 08e70387df9f7e5518c7e6efc08904f284658a03..9f48d3dca87f92ef942fefc26a8c68da5c9bb0b6 100644 (file)
--- a/avr/cli.c
+++ b/avr/cli.c
@@ -1,3 +1,17 @@
+/*
+ * (C) Copyright 2014-2016 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Add to readline cmdline-editing by
+ * (C) Copyright 2005
+ * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#include "cli.h"
 #include "common.h"
 
 #include <string.h>
 #include "env.h"
 #include "cli_readline.h"
 #include "con-utils.h"
-#include "cli.h"
+
+
+/* FIXME: Quoting problems */
 
 #define DEBUG_PARSER   0       /* set to 1 to debug */
 
 #define debug_parser(fmt, args...)             \
        debug_cond(DEBUG_PARSER, fmt, ##args)
 
-static int cli_parse_line(char *line, char *argv[])
+
+static bool opt_xtrace;
+static bool opt_verbose;
+static int_least8_t command_level;
+
+static void cli_trace_cmd(int_fast8_t level, int argc, char *argv[])
 {
-       static const FLASH char delim[] = {" \t"};
+       while (level-- > 0)
+               putchar('+');
+       for (int_fast8_t i = 0; i < argc; i++)
+               printf_P(PSTR(" %s"), argv[i]);
+       putchar('\n');
+}
+
 
-       char *ptr;
+
+
+static int cli_parse_line(char *line, char *argv[])
+{
+       uint_fast8_t state = 0;
        uint_fast8_t nargs = 0;
+       char *inp, *outp;
+       char c, quote;
+
+       debug_parser("%s: \"%s\"\n", __func__, line);
+
+       for (outp = inp = line, quote = '\0'; (c = *inp) != '\0'; inp++) {
+
+               switch (state) {
+               case 0:                                         /* before arg string, waiting for arg start */
+                       if (isblank(c))
+                               continue;
+
+                       argv[nargs++] = inp;    /* begin of argument string     */
+                       outp = inp;
+                       state = 1;
+                       /* fall thru */
+
+               case 1:                                         /* in arg string, waiting for end of arg string */
+                       if (c == '\\') {
+                               ++state;
+                               continue;
+                       }
+                       if (c == '\"' || c == '\'') {
+                               quote = c;
+                               state = 3;
+                               continue;
+                       }
+                       if (isblank(c)) {
+                               c = '\0';
+                               state = 0;
+                       }
+                       break;
+
+               case 3:                                         /* in quote */
+                       if (c == '\\' && quote == '\"') {
+                               ++state;
+                               continue;
+                       }
+                       if (c == quote) {
+                               state = 1;
+                               continue;
+                       }
+                       break;
 
-       debug_parser("parse_line: \"%s\"\n", line);
+               case 2:                                         /* waiting for next char */
+               case 4:
+                       --state;
+                       break;
+
+               }
 
-       ptr = strtok_P(line, delim);
-       while(nargs < CONFIG_SYS_MAXARGS && ptr != NULL) {
-               argv[nargs++] = ptr;
-               ptr = strtok_P(NULL, delim);
+               if (nargs > CONFIG_SYS_MAXARGS) {
+                       --nargs;
+                       break;
+               }
+               *outp++ = c;
        }
 
-       if (ptr != NULL)
+       if (*inp != '\0')
                printf_P(PSTR("** Too many args (max. %d) **\n"), CONFIG_SYS_MAXARGS);
 
+       *outp = '\0';
        argv[nargs] = NULL;
-       debug_parser("parse_line: nargs=%d\n", nargs);
-
+       debug_parser("%s: nargs=%d\n", __func__, nargs);
+#if 0
+       for (int i = 0; i < nargs; i++)
+               debug_parser("%s: arg %d: >%s<\n", __func__, i, argv[i]);
+#endif
        return nargs;
+
 }
 
-static void process_macros(const char *input, char *output)
+static
+void append_char(uint_fast8_t pass, char **p, char c)
 {
-       char c, prev;
-       const char *varname_start = NULL;
-#if 0 && (CONFIG_SYS_CBSIZE < __UINT_FAST8_MAX__)
-       uint_fast8_t inputcnt = strlen(input);
-       uint_fast8_t outputcnt = CONFIG_SYS_CBSIZE;
-#else
-       int inputcnt = strlen(input);
-       int outputcnt = CONFIG_SYS_CBSIZE;
-#endif
-       uint_fast8_t state = 0;         /* 0 = waiting for '$'  */
 
-       /* 1 = waiting for '{' */
-       /* 2 = waiting for '}' */
-       /* 3 = waiting for '''  */
-       char *output_start = output;
+       if (pass) {
+               **p = c;
+       }
+       ++(*p);
+}
 
-       debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input),
-                    input);
+static
+char *process_macros(char *input, char *output)
+{
+       char c, prev, *inp, *outp;
+       const char *varname = NULL;
+
+       for(uint_fast8_t pass = 0; pass < 2; pass++)
+       {
+               uint_fast8_t state = 0;
+               /* 0 = waiting for '$'  */
+               /* 1 = waiting for '{' */
+               /* 2 = waiting for '}' */
+               /* 3 = waiting for ''' */
+
+               if (pass == 0) {
+                       outp = output;
+               } else {
+                       int outputlen = outp - output;
+                       outp = xrealloc(output, outputlen);
+                       output = outp;
+               }
 
-       prev = '\0';            /* previous character   */
+               inp = input;
 
-       while (inputcnt && outputcnt) {
-               c = *input++;
-               inputcnt--;
+               debug_parser("[PROCESS_MACROS] INPUT len %d: \"%s\"\n",
+                                               strlen(inp), inp);
 
-               if (state != 3) {
-                       /* remove one level of escape characters */
-                       if ((c == '\\') && (prev != '\\')) {
-                               if (inputcnt-- == 0)
-                                       break;
-                               prev = c;
-                               c = *input++;
-                       }
-               }
+               for (prev = '\0'; (c = *inp++) != '\0'; prev = c) {
 
-               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 == '{') {
-                               state++;
-                               varname_start = input;
-                       } else {
-                               state = 0;
-                               *(output++) = '$';
-                               outputcnt--;
 
-                               if (outputcnt) {
-                                       *(output++) = c;
-                                       outputcnt--;
+
+                       switch (state) {
+                       case 0: /* Waiting for (unescaped) $    */
+                               if ((c == '\'') && (prev != '\\')) {
+                                       state = 3;
+                                       break;
                                }
+                               if ((c == '$') && (prev != '\\')) {
+                                       state++;
+                                       continue;
+                               }
+                               break;
+                       case 1: /* Waiting for {        */
+                               if (c == '{') {
+                                       state++;
+                                       varname = inp;
+                                       continue;
+                               } else {
+                                       state = 0;
+                                       append_char(pass, &outp, '$');
+                               }
+                               break;
+                       case 2: /* Waiting for }        */
+                               if (c == '}') {
+                                       /* Terminate variable name */
+                                       *(inp-1) = '\0';
+                                       const char *envval = getenv_str(varname);
+                                       *(inp-1) = '}';
+                                       /* Copy into the line if it exists */
+                                       if (envval != NULL)
+                                               while (*envval)
+                                                       append_char(pass, &outp, *(envval++));
+                                       /* Look for another '$' */
+                                       state = 0;
+                               }
+                               continue;
+                       case 3: /* Waiting for '        */
+                               if (c == '\'')
+                                       state = 0;
+                               break;
                        }
-                       break;
-               case 2: /* Waiting for )        */
-                       if (c == '}') {
-                               char envname[CONFIG_SYS_ENV_NAMELEN+1], *envval;
-                               /* Varname # of chars */
-                               uint_fast8_t envcnt = input - varname_start - 1;
-                               if (envcnt > CONFIG_SYS_ENV_NAMELEN)
-                                       envcnt = CONFIG_SYS_ENV_NAMELEN;
-
-                               memcpy(envname, varname_start, envcnt);
-                               envname[envcnt] = '\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;
+                       append_char(pass, &outp, c);
                }
-               prev = c;
-       }
 
-       if (outputcnt)
-               *output = 0;
-       else
-               *(output - 1) = 0;
+               append_char(pass, &outp, 0);
+       }
 
        debug_parser("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
-                    strlen(output_start), output_start);
+                    strlen(output), output);
+
+       return output;
 }
 
 /**
@@ -159,7 +227,7 @@ static void process_macros(const char *input, char *output)
  * 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
+ * may be the result from getenv_str(), 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).
  *
@@ -174,26 +242,37 @@ static int cli_run_command(const char *cmd, int flag)
        char *cmdbuf;                   /* working copy of cmd          */
        char *token;                    /* start of token in cmdbuf     */
        char *sep;                      /* end of token (separator) in cmdbuf */
-       char *finaltoken;
+       char *finaltoken = NULL;        /* token after macro expansion  */
        char *str;
        char *argv[CONFIG_SYS_MAXARGS + 1];     /* NULL terminated      */
        int argc;
        uint_fast8_t inquotes, repeatable = 1;
        int rc = 0;
 
+       opt_verbose = 0;
+       opt_xtrace = 0;
+       char *optenv = getenv_str(PSTR("cli"));
+       if (optenv) {
+               opt_verbose = (strstr_P(optenv, PSTR("verbose")) != NULL);
+               opt_xtrace = (strstr_P(optenv, PSTR("xtrace")) != NULL);
+       }
+
        debug_parser("[RUN_COMMAND] cmd[%p]=\"%s\"\n",
                        cmd, cmd ? cmd : "NULL");
 
+       if (opt_verbose)
+               printf_P(PSTR("%s\n"), cmd, cmd ? cmd : "");
+
        clear_ctrlc();          /* forget any previous Control C */
 
        if (!cmd || !*cmd)
                return -1;      /* empty command */
 
        cmdbuf = strdup(cmd);
-       finaltoken = xmalloc(CONFIG_SYS_CBSIZE);
-       if (!finaltoken)
+       if (!cmdbuf)
                return -1;      /* not enough memory */
 
+       ++command_level;
        str = cmdbuf;
 
        /* Process separators and check for invalid
@@ -208,40 +287,51 @@ static int cli_run_command(const char *cmd, int flag)
                 */
                for (inquotes = 0, sep = str; *sep; sep++) {
                        if ((*sep == '\'') &&
-                           (*(sep - 1) != '\\'))
+                           (sep != str) &&                                     /* past string start */
+                           (*(sep - 1) != '\\'))                       /* and NOT escaped */
                                inquotes = !inquotes;
 
                        if (!inquotes &&
-                           (*sep == ';' || *sep == '\n') &&    /* separator            */
-                           (sep != str) &&                     /* past string start    */
-                           (*(sep - 1) != '\\'))               /* and NOT escaped */
+                           (*sep == ';' || *sep == '\n'        /* separator */
+                                       || *sep == '#') &&                      /*   or start of comment */
+                           ((sep == str) ||                            /* string start */
+                           (*(sep - 1) != '\\')))                      /*   or NOT escaped */
                                break;
                }
 
-               /*
-                * Limit the token to data between separators
-                */
+               /* no more commands after unescaped '#' token */
+               if (*sep == '#')
+                       *sep = '\0';
+
+               /* Limit the token to data between separators */
                token = str;
                if (*sep) {
-                       str = sep + 1;  /* start of command for next pass */
+                       str = sep + 1;          /* start of command for next pass */
                        *sep = '\0';
                } else {
-                       str = sep;      /* no more commands for next pass */
+                       str = sep;                      /* no more commands for next pass */
                }
                debug_parser("token: \"%s\"\n", token);
 
                /* find macros in this token and replace them */
-               process_macros(token, finaltoken);
+               finaltoken = process_macros(token, finaltoken);
 
                /* Extract arguments */
                argc = cli_parse_line(finaltoken, argv);
                if (argc == 0) {
-                       rc = -1;        /* no command at all */
-                       continue;
+                       //rc = -1;
+                       continue; /* no command at all */
                }
 
-               if (cmd_process(flag, argc, argv, &repeatable) != CMD_RET_SUCCESS)
+               if (opt_xtrace)
+                       cli_trace_cmd(command_level, argc, argv);
+
+               rc = cmd_process(flag, argc, argv, &repeatable);
+               if (rc != CMD_RET_SUCCESS) {
+                       if (opt_verbose)
+                               printf_P(PSTR("Command failed, result=%d\n"), rc);
                        rc = -1;
+               }
 
                /* Did the user stop this? */
                if (had_ctrlc()) {
@@ -252,6 +342,7 @@ static int cli_run_command(const char *cmd, int flag)
 
        free(cmdbuf);
        free(finaltoken);
+       --command_level;
 
        return rc ? rc : repeatable;
 }
@@ -265,7 +356,7 @@ static int cli_run_command_list(const char *cmd)
 
 
 /*
- * Run a command using the selected parser.
+ * Run a command.
  *
  * @param cmd  Command to run
  * @param flag Execution flags (CMD_FLAG_...)
@@ -313,7 +404,7 @@ void cli_loop(void)
        int rc = 1;
 
        for (;;) {
-               len = cli_readline(PSTR(CONFIG_SYS_PROMPT));
+               len = cli_readline(lastcommand ? PSTR(CONFIG_SYS_PROMPT_REPEAT) : PSTR(CONFIG_SYS_PROMPT), 1);
 
                flag = 0;       /* assume no special flags for now */
                if (len > 0) {
@@ -322,9 +413,10 @@ void cli_loop(void)
                } else if (len == 0)
                        flag |= CMD_FLAG_REPEAT;
 
-               if (len == -1)
-                       my_puts_P(PSTR("<INTERRUPT>\n"));
-               else
+               if (len == -1) {
+                       if (opt_verbose)
+                               my_puts_P(PSTR("<INTERRUPT>\n"));
+               } else
                        rc = run_command_repeatable(lastcommand, flag);
 
                if (rc <= 0) {
@@ -334,28 +426,3 @@ void cli_loop(void)
                }
        }
 }
-
-
-command_ret_t do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
-       int i;
-       (void) cmdtp;
-
-       if (argc < 2)
-               return CMD_RET_USAGE;
-
-       for (i = 1; i < argc; ++i) {
-               char *arg;
-
-               arg = getenv(argv[i]);
-               if (arg == NULL) {
-                       printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]);
-                       return CMD_RET_FAILURE;
-               }
-
-               if (run_command(arg, flag) != 0)
-                       return CMD_RET_FAILURE;
-       }
-       return CMD_RET_SUCCESS;
-}
-