Summary: passing of variable argument list reduced, strings sent to logging are now...
[fw/openocd] / src / helper / command.c
index 00d4b356150abe31a5ea40b82847a8fa490617b0..aa71f0ba18c1b2af47436423181638143ab0dd20 100644 (file)
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "replacements.h"
+
 #include "command.h"
 
 #include "log.h"
+#include "time_support.h"
 
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
 
+void command_print_help_line(command_context_t* context, struct command_s *command, int indent);
+
 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
 
 int build_unique_lengths(command_context_t *context, command_t *commands)
 {
@@ -70,9 +80,15 @@ int build_unique_lengths(command_context_t *context, command_t *commands)
        return ERROR_OK;
 }
 
+/* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n). 
+ * Makes a difference on ARM7 types machines and is not observable on GHz machines.
+ */
+static int unique_length_dirty = 1; 
+
 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
 {
        command_t *c, *p;
+       unique_length_dirty = 1;
        
        if (!context || !name)
                return NULL;
@@ -121,14 +137,13 @@ command_t* register_command(command_context_t *context, command_t *parent, char
                }
        }
        
-       /* update unique lengths */
-       build_unique_lengths(context, (parent) ? parent : context->commands);
-       
        return c;
 }
 
 int unregister_command(command_context_t *context, char *name)
 {
+       unique_length_dirty = 1;
+       
        command_t *c, *p = NULL, *c2;
        
        if ((!context) || (!name))
@@ -194,14 +209,24 @@ int parse_line(char *line, char *words[], int max_words)
                        /* we're inside a word or quote, and reached its end*/
                        if (word_start)
                        {
-                               int len = p - word_start;
-
-                               /* copy the word */
-                               memcpy(words[nwords] = malloc(len + 1), word_start, len);
-                               /* add terminating NUL */
-                               words[nwords++][len] = 0;
+                               int len;
+                               char *word_end=p;
+                               
+                               /* This will handle extra whitespace within quotes */
+                               while (isspace(*word_start)&&(word_start<word_end))
+                                       word_start++;
+                               while (isspace(*(word_end-1))&&(word_start<word_end))
+                                       word_end--;
+                               len = word_end - word_start;
+                               
+                               if (len>0)
+                               {
+                                       /* copy the word */
+                                       memcpy(words[nwords] = malloc(len + 1), word_start, len);
+                                       /* add terminating NUL */
+                                       words[nwords++][len] = 0;
+                               }
                        }
-
                        /* we're done parsing the line */
                        if (!*p)
                                break;
@@ -209,7 +234,9 @@ int parse_line(char *line, char *words[], int max_words)
                        /* skip over trailing quote or whitespace*/
                        if (inquote || isspace(*p))
                                p++;
-
+                       while (isspace(*p))
+                               p++;
+                       
                        inquote = 0;
                        word_start = 0;
                }
@@ -233,48 +260,38 @@ int parse_line(char *line, char *words[], int max_words)
        return nwords;
 }
 
-void command_print(command_context_t *context, char *format, ...)
+void command_print_n(command_context_t *context, char *format, ...)
 {
+       char *string;
+       
        va_list ap;
-       char *buffer = NULL;
-       int n, size = 0;
-       char *p;
-
        va_start(ap, format);
-       
-       /* process format string */
-       /* TODO: possible bug. va_list is undefined after the first call to vsnprintf */
-       while (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)
-       {
-               /* increase buffer until it fits the whole string */
-               if (!(p = realloc(buffer, size += 4096)))
-                       return;
 
-               buffer = p;
+       string = alloc_printf(format, ap);
+       if (string != NULL)
+       {
+               context->output_handler(context, string);
+               free(string);
        }
-       
-       /* vsnprintf failed */
-       if (n < 0)
-               return;
 
-       p = buffer;
-       
-       /* process lines in buffer */
-       do {
-               char *next = strchr(p, '\n');
-               
-               if (next)
-                       *next++ = 0;
+       va_end(ap);
+}
 
-               if (context->output_handler)
-                       context->output_handler(context, p);
+void command_print(command_context_t *context, char *format, ...)
+{
+       char *string;
+
+       va_list ap;
+       va_start(ap, format);
+
+       string = alloc_printf(format, ap);
+       if (string != NULL)
+       {
+               strcat(string, "\n"); /* alloc_printf guaranteed the buffer to be at least one char longer */
+               context->output_handler(context, string);
+               free(string);
+       }
 
-               p = next;
-       } while (p);
-       
-       if (buffer)
-               free(buffer);
-       
        va_end(ap);
 }
 
@@ -282,6 +299,13 @@ int find_and_run_command(command_context_t *context, command_t *commands, char *
 {
        command_t *c;
        
+       if (unique_length_dirty)
+       {
+               unique_length_dirty = 0;
+               /* update unique lengths */
+               build_unique_lengths(context, context->commands);
+       }
+       
        for (c = commands; c; c = c->next)
        {
                if (strncasecmp(c->name, words[start_word], c->unique_len))
@@ -290,7 +314,7 @@ int find_and_run_command(command_context_t *context, command_t *commands, char *
                if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
                        continue;
                
-               if ((c->mode == context->mode) || (c->mode == COMMAND_ANY))
+               if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
                {
                        if (!c->children)
                        {
@@ -301,7 +325,13 @@ int find_and_run_command(command_context_t *context, command_t *commands, char *
                                }
                                else
                                {
-                                       return c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
+                                       int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
+                                       if (retval == ERROR_COMMAND_SYNTAX_ERROR)
+                                       {
+                                               command_print(context, "Syntax error:");
+                                               command_print_help_line(context, c, 0);
+                                       }
+                                       return retval; 
                                }
                        }
                        else
@@ -338,10 +368,11 @@ int command_run_line(command_context_t *context, char *line)
        if (!*line)
                return ERROR_OK;
        
-       if (context->echo)
-       {
-               command_print(context, "%s", line);
-       }
+       /* ignore comments */
+       if (*line && (line[0] == '#'))
+               return ERROR_OK;
+       
+       DEBUG("%s", line);
 
        nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
        
@@ -358,9 +389,13 @@ int command_run_line(command_context_t *context, char *line)
 
 int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
 {
-       int retval;
+       int retval = ERROR_OK;
        int old_command_mode;
-       char buffer[4096];
+       char *buffer=malloc(4096);
+       if (buffer==NULL)
+       {
+               return ERROR_INVALID_ARGUMENTS;
+       }
        
        old_command_mode = context->mode;
        context->mode = mode;
@@ -394,11 +429,14 @@ int command_run_file(command_context_t *context, FILE *file, enum command_mode m
                        break;
 
                /* run line */
-               if (command_run_line(context, cmd) == ERROR_COMMAND_CLOSE_CONNECTION)
+               if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
                        break;
        }
        
        context->mode = old_command_mode;
+
+       
+       free(buffer);
        
        return retval;
 }
@@ -406,27 +444,27 @@ int command_run_file(command_context_t *context, FILE *file, enum command_mode m
 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
 {
        command_t *c;
-       char indents[32] = {0};
+       char indent_text[indent + 2];
        char *help = "no help available";
        char name_buf[64];
        int i;
        
-       for (i = 0; i < indent; i+=2)
+       if (indent)
        {
-               indents[i*2] = ' ';
-               indents[i*2+1] = '-';
+           indent_text[0] = ' ';
+           memset(indent_text + 1, '-', indent);
+           indent_text[indent + 1] = 0;
        }
-       indents[i*2] = 0;
        
-       if ((command->mode == COMMAND_EXEC) || (command->mode == COMMAND_ANY))
-       {
-               if (command->help)
-                       help = command->help;
+       if (command->help)
+               help = command->help;
                
-               snprintf(name_buf, 64, command->name);
-               strncat(name_buf, indents, 64);
-               command_print(context, "%20s\t%s", name_buf, help);
-       }
+       snprintf(name_buf, 64, command->name);
+
+       if (indent)
+           strncat(name_buf, indent_text, 64);
+
+       command_print(context, "%20s\t%s", name_buf, help, indent);
        
        if (command->children)
        {
@@ -437,18 +475,40 @@ void command_print_help_line(command_context_t* context, struct command_s *comma
        }
 }
 
-int command_print_help(command_context_t* context, char* name, char** args, int argc)
+int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
 {
-       command_t *c;
+       command_t * c;
+       int i;
 
-       for (c = context->commands; c; c = c->next)
+       for (c = c_first; c; c = c->next)
        {
+               if (argc > 0)
+               {
+                       if (strncasecmp(c->name, args[0], c->unique_len))
+                               continue;
+
+                       if (strncasecmp(c->name, args[0], strlen(args[0])))
+                               continue;
+
+                       if (argc > 1)
+                       {
+                               command_print_help_match(context, c->children, name, args + 1, argc - 1);
+                               continue;
+                       }
+               }
+
                command_print_help_line(context, c, 0);
        }
        
        return ERROR_OK;
 }
 
+int command_print_help(command_context_t* context, char* name, char** args, int argc)
+{
+    return command_print_help_match(context, context->commands, name, args, argc);
+}
+
+
 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
 {
        context->output_handler = output_handler;
@@ -467,6 +527,7 @@ command_context_t* copy_command_context(command_context_t* context)
 int command_done(command_context_t *context)
 {
        free(context);
+       context = NULL;
        
        return ERROR_OK;
 }
@@ -478,7 +539,6 @@ command_context_t* command_init()
        context->mode = COMMAND_EXEC;
        context->commands = NULL;
        context->current_target = 0;
-       context->echo = 0;
        context->output_handler = NULL;
        context->output_handler_priv = NULL;
        
@@ -488,6 +548,9 @@ command_context_t* command_init()
        register_command(context, NULL, "sleep", handle_sleep_command,
                                         COMMAND_ANY, "sleep for <n> milliseconds");
        
+       register_command(context, NULL, "time", handle_time_command,
+                                        COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
+       
        return context;
 }
 
@@ -506,3 +569,27 @@ int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **ar
 
        return ERROR_OK;
 }
+
+int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+{
+       if (argc<1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       
+       duration_t duration;
+       char *duration_text;
+       int retval;
+       
+       duration_start_measure(&duration);
+       
+       retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);
+       
+       duration_stop_measure(&duration, &duration_text);
+       
+       float t=duration.duration.tv_sec;
+       t+=((float)duration.duration.tv_usec / 1000000.0);
+       command_print(cmd_ctx, "%s took %fs", args[0], t);
+       
+       free(duration_text);
+
+       return retval;
+}