X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Fhelper%2Fcommand.c;h=3ec1f84d60e25c48a99cc98450b26e004ee0ecb9;hb=0ebb33b185314a113a6961bf26c7b59cf42aff63;hp=e9ea06927759558e72a4189ce3e36ca361af92bd;hpb=64aac5f7e1f2893d88fc4fb7079829de74c41920;p=openocd.git diff --git a/src/helper/command.c b/src/helper/command.c index e9ea069277..3ec1f84d60 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -29,6 +29,7 @@ #include "command.h" #include "log.h" +#include "time_support.h" #include #include @@ -37,54 +38,76 @@ #include #include +#include + +int fast_and_dangerous = 0; + int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc); +static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string) +{ + Jim_Obj *tclOutput=(Jim_Obj *)privData; + + Jim_AppendString(interp, tclOutput, string, strlen(string)); +} -int build_unique_lengths(command_context_t *context, command_t *commands) +static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - command_t *c, *p; + /* the private data is stashed in the interp structure */ + command_t *c; + command_context_t *context; + int *retval; + int i; + int nwords; + char **words; + + target_call_timer_callbacks_now(); + LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ + + c = interp->cmdPrivData; + LOG_DEBUG("script_command - %s", c->name); - /* iterate through all commands */ - for (c = commands; c; c = c->next) + nwords = argc; + words = malloc(sizeof(char *) * nwords); + for (i = 0; i < nwords; i++) { - /* find out how many characters are required to uniquely identify a command */ - for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++) + int len; + + words[i] = strdup(Jim_GetString(argv[i], &len)); + if (words[i] == NULL) { - int foundmatch = 0; - - /* for every command, see if the current length is enough */ - for (p = commands; p; p = p->next) - { - /* ignore the command itself */ - if (c == p) - continue; - - /* compare commands up to the current length */ - if (strncmp(p->name, c->name, c->unique_len) == 0) - foundmatch++; - } - - /* when none of the commands matched, we've found the minimum length required */ - if (!foundmatch) - break; + return JIM_ERR; } + LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]); + } + + /* grab the command context from the associated data */ + context = Jim_GetAssocData(interp, "context"); + retval = Jim_GetAssocData(interp, "retval"); + if (context != NULL && retval != NULL) + { + /* capture log output and return it */ + Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0); + log_add_callback(tcl_output, tclOutput); - /* if the current command has children, build the unique lengths for them */ - if (c->children) - build_unique_lengths(context, c->children); + *retval = run_command(context, c, words, nwords); + + log_remove_callback(tcl_output, tclOutput); + + /* We dump output into this local variable */ + Jim_SetVariableStr(interp, "openocd_output", tclOutput); } - - 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; + for (i = 0; i < nwords; i++) + free(words[i]); + free(words); + + return (*retval==ERROR_OK)?JIM_OK:JIM_ERR; +} 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; @@ -96,11 +119,8 @@ command_t* register_command(command_context_t *context, command_t *parent, char c->children = NULL; c->handler = handler; c->mode = mode; - if (help) - c->help = strdup(help); - else - c->help = NULL; - c->unique_len = 0; + if (!help) + help=""; c->next = NULL; /* place command in tree */ @@ -133,13 +153,88 @@ command_t* register_command(command_context_t *context, command_t *parent, char } } + /* just a placeholder, no handler */ + if (c->handler==NULL) + return c; + + /* If this is a two level command, e.g. "flash banks", then the + * "unknown" proc in startup.tcl must redirect to this command. + * + * "flash banks" is translated by "unknown" to "flash_banks" + * if such a proc exists + */ + /* Print help for command */ + const char *t1=""; + const char *t2=""; + const char *t3=""; + /* maximum of two levels :-) */ + if (c->parent!=NULL) + { + t1=c->parent->name; + t2="_"; + } + t3=c->name; + const char *full_name=alloc_printf("%s%s%s", t1, t2, t3); + Jim_CreateCommand(interp, full_name, script_command, c, NULL); + free((void *)full_name); + + + /* accumulate help text in Tcl helptext list. */ + Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG); + if (Jim_IsShared(helptext)) + helptext = Jim_DuplicateObj(interp, helptext); + Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0); + + Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0); + + /* maximum of two levels :-) */ + if (c->parent!=NULL) + { + Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1)); + } + Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1)); + + Jim_ListAppendElement(interp, cmd_entry, cmd_list); + Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1)); + Jim_ListAppendElement(interp, helptext, cmd_entry); return c; } -int unregister_command(command_context_t *context, char *name) +int unregister_all_commands(command_context_t *context) { - unique_length_dirty=1; + command_t *c, *c2; + + if (context == NULL) + return ERROR_OK; + + + while(NULL != context->commands) + { + c = context->commands; + + while(NULL != c->children) + { + c2 = c->children; + c->children = c->children->next; + free(c2->name); + c2->name = NULL; + free(c2); + c2 = NULL; + } + + context->commands = context->commands->next; + + free(c->name); + c->name = NULL; + free(c); + c = NULL; + } + return ERROR_OK; +} + +int unregister_command(command_context_t *context, char *name) +{ command_t *c, *p = NULL, *c2; if ((!context) || (!name)) @@ -166,16 +261,12 @@ int unregister_command(command_context_t *context, char *name) for (c2 = c->children; c2; c2 = c2->next) { free(c2->name); - if (c2->help) - free(c2->help); free(c2); } } /* delete command */ free(c->name); - if (c->help) - free(c->help); free(c); } @@ -186,329 +277,183 @@ int unregister_command(command_context_t *context, char *name) return ERROR_OK; } -int parse_line(char *line, char *words[], int max_words) + +void command_output_text(command_context_t *context, const char *data) { - int nwords = 0; - char *p = line; - char *word_start = line; - int inquote = 0; + if( context && context->output_handler && data ){ + context->output_handler( context, data ); + } +} + +void command_print_n(command_context_t *context, char *format, ...) +{ + char *string; + + va_list ap; + va_start(ap, format); - while (nwords < max_words - 1) + string = alloc_vprintf(format, ap); + if (string != NULL) { - /* check if we reached - * a terminating NUL - * a matching closing quote character " or ' - * we're inside a word but not a quote, and the current character is whitespace + /* we want this collected in the log + we also want to pick it up as a tcl return + * value. + * + * The latter bit isn't precisely neat, but will do for now. */ - if (!*p || *p == inquote || (word_start && !inquote && isspace(*p))) - { - /* we're inside a word or quote, and reached its end*/ - if (word_start) - { - int len; - char *word_end=p; - - /* This will handle extra whitespace within quotes */ - while (isspace(*word_start)&&(word_start0) - { - /* 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; - - /* skip over trailing quote or whitespace*/ - if (inquote || isspace(*p)) - p++; - while (isspace(*p)) - p++; - - inquote = 0; - word_start = 0; - } - else if (*p == '"' || *p == '\'') - { - /* we've reached the beginning of a quote */ - inquote = *p++; - word_start = p; - } - else - { - /* we've reached the beginning of a new word */ - if (!word_start) - word_start = p; - - /* normal character, skip */ - p++; - } + LOG_USER_N("%s", string); + // We already printed it above + //command_output_text(context, string); + free(string); } - - return nwords; + + va_end(ap); } void command_print(command_context_t *context, char *format, ...) { - va_list ap; - char *buffer = NULL; - int n, size = 0; - char *p; + char *string; + va_list ap; 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))) - { - /* gotta free up */ - if (buffer) - free(buffer); - return; - } - buffer = p; - } - - /* vsnprintf failed */ - if (n < 0) + string = alloc_vprintf(format, ap); + if (string != NULL) { - if (buffer) - free(buffer); - return; + strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */ + /* we want this collected in the log + we also want to pick it up as a tcl return + * value. + * + * The latter bit isn't precisely neat, but will do for now. + */ + LOG_USER_N("%s", string); + // We already printed it above + //command_output_text(context, string); + free(string); } - p = buffer; - - /* process lines in buffer */ - do { - char *next = strchr(p, '\n'); - - if (next) - *next++ = 0; - - if (context->output_handler) - context->output_handler(context, p); - - p = next; - } while (p); - - if (buffer) - free(buffer); - va_end(ap); } -int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word) +int run_command(command_context_t *context, command_t *c, char *words[], int num_words) { - command_t *c; - - if (unique_length_dirty) + int start_word=0; + if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )) { - unique_length_dirty=0; - /* update unique lengths */ - build_unique_lengths(context, context->commands); + /* Config commands can not run after the config stage */ + return ERROR_FAIL; } - for (c = commands; c; c = c->next) + int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1); + if (retval == ERROR_COMMAND_SYNTAX_ERROR) { - if (strncasecmp(c->name, words[start_word], c->unique_len)) - continue; - - if (strncasecmp(c->name, words[start_word], strlen(words[start_word]))) - continue; - - if ((c->mode == context->mode) || (c->mode == COMMAND_ANY)) + /* Print help for command */ + const char *t1=""; + const char *t2=""; + const char *t3=""; + /* maximum of two levels :-) */ + if (c->parent!=NULL) { - if (!c->children) - { - if (!c->handler) - { - command_print(context, "No handler for command"); - break; - } - else - { - return c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1); - } - } - else - { - if (start_word == num_words - 1) - { - command_print(context, "Incomplete command"); - break; - } - return find_and_run_command(context, c->children, words, num_words, start_word + 1); - } + t1=c->parent->name; + t2=" "; } + t3=c->name; + command_run_linef(context, "help {%s%s%s}", t1, t2, t3); } - - command_print(context, "Command %s not found", words[start_word]); - return ERROR_OK; -} - -int command_run_line(command_context_t *context, char *line) -{ - int nwords; - char *words[128] = {0}; - int retval; - int i; - - if ((!context) || (!line)) - return ERROR_INVALID_ARGUMENTS; - - /* skip preceding whitespace */ - while (isspace(*line)) - line++; - - /* empty line, ignore */ - if (!*line) - return ERROR_OK; - - /* ignore comments */ - if (*line && (line[0] == '#')) - return ERROR_OK; - - if (context->echo) + else if (retval == ERROR_COMMAND_CLOSE_CONNECTION) { - command_print(context, "%s", line); + /* just fall through for a shutdown request */ } - - nwords = parse_line(line, words, sizeof(words) / sizeof(words[0])); - - if (nwords > 0) - retval = find_and_run_command(context, context->commands, words, nwords, 0); - else - return ERROR_INVALID_ARGUMENTS; - - for (i = 0; i < nwords; i++) - free(words[i]); - - return retval; -} - -int command_run_file(command_context_t *context, FILE *file, enum command_mode mode) -{ - int retval = ERROR_OK; - int old_command_mode; - char *buffer=malloc(4096); - if (buffer==NULL) + else if (retval != ERROR_OK) { - return ERROR_INVALID_ARGUMENTS; - } - - old_command_mode = context->mode; - context->mode = mode; - - while (fgets(buffer, 4096, file)) - { - char *p; - char *cmd, *end; - - /* stop processing line after a comment (#, !) or a LF, CR were encountered */ - if ((p = strpbrk(buffer, "#!\r\n"))) - *p = 0; - - /* skip over leading whitespace */ - cmd = buffer; - while (isspace(*cmd)) - cmd++; - - /* empty (all whitespace) line? */ - if (!*cmd) - continue; - - /* search the end of the current line, ignore trailing whitespace */ - for (p = end = cmd; *p; p++) - if (!isspace(*p)) - end = p; - - /* terminate end */ - *++end = 0; - if (strcasecmp(cmd, "quit") == 0) - break; - - /* run line */ - if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION) - break; + /* we do not print out an error message because the command *should* + * have printed out an error + */ + LOG_DEBUG("Command failed with error code %d", retval); } - context->mode = old_command_mode; - - - free(buffer); - - return retval; + return retval; } -void command_print_help_line(command_context_t* context, struct command_s *command, int indent) +int command_run_line(command_context_t *context, char *line) { - command_t *c; - char indents[32] = {0}; - char *help = "no help available"; - char name_buf[64]; - int i; - - for (i = 0; i < indent; i+=2) - { - indents[i*2] = ' '; - indents[i*2+1] = '-'; - } - indents[i*2] = 0; - - if ((command->mode == COMMAND_EXEC) || (command->mode == COMMAND_ANY)) - { - 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); - } - - if (command->children) - { - for (c = command->children; c; c = c->next) + /* all the parent commands have been registered with the interpreter + * so, can just evaluate the line as a script and check for + * results + */ + /* run the line thru a script engine */ + int retval; + int retcode; + Jim_DeleteAssocData(interp, "context"); /* remove existing */ + retcode = Jim_SetAssocData(interp, "context", NULL, context); + if (retcode != JIM_OK) + return ERROR_FAIL; + + /* associated the return value */ + retval = ERROR_OK; + Jim_DeleteAssocData(interp, "retval"); /* remove existing */ + retcode = Jim_SetAssocData(interp, "retval", NULL, &retval); + if (retcode != JIM_OK) + return ERROR_FAIL; + + retcode = Jim_Eval(interp, line); + if (retcode == JIM_ERR) { + if (retval!=ERROR_COMMAND_CLOSE_CONNECTION) + { + /* We do not print the connection closed error message */ + Jim_PrintErrorMessage(interp); + } + if (retval==ERROR_OK) { - command_print_help_line(context, c, indent + 1); + /* It wasn't a low level OpenOCD command that failed */ + return ERROR_FAIL; + } + return retval; + } else if (retcode == JIM_EXIT) { + /* ignore. */ + /* exit(Jim_GetExitCode(interp)); */ + } else { + const char *result; + int reslen; + + result = Jim_GetString(Jim_GetResult(interp), &reslen); + if (reslen) { + int i; + char buff[256+1]; + for (i = 0; i < reslen; i += 256) + { + int chunk; + chunk = reslen - i; + if (chunk > 256) + chunk = 256; + strncpy(buff, result+i, chunk); + buff[chunk] = 0; + LOG_USER_N("%s", buff); + } + LOG_USER_N("%s", "\n"); } } + return retval; } -int command_print_help(command_context_t* context, char* name, char** args, int argc) -{ - command_t *c; - for (c = context->commands; c; c = c->next) +int command_run_linef(command_context_t *context, char *format, ...) +{ + int retval=ERROR_FAIL; + char *string; + va_list ap; + va_start(ap, format); + string = alloc_vprintf(format, ap); + if (string!=NULL) { - if (argc == 1) - { - if (strncasecmp(c->name, args[0], c->unique_len)) - continue; - - if (strncasecmp(c->name, args[0], strlen(args[0]))) - continue; - } - - command_print_help_line(context, c, 0); + retval=command_run_line(context, string); } - - return ERROR_OK; + va_end(ap); + return retval; } -void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv) + + +void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv) { context->output_handler = output_handler; context->output_handler_priv = priv; @@ -519,7 +464,7 @@ command_context_t* copy_command_context(command_context_t* context) command_context_t* copy_context = malloc(sizeof(command_context_t)); *copy_context = *context; - + return copy_context; } @@ -538,19 +483,27 @@ 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; - register_command(context, NULL, "help", command_print_help, - COMMAND_EXEC, "display this help"); - register_command(context, NULL, "sleep", handle_sleep_command, COMMAND_ANY, "sleep for milliseconds"); + register_command(context, NULL, "fast", handle_fast_command, + COMMAND_ANY, "fast - place at beginning of config files. Sets defaults to fast and dangerous."); + return context; } +int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode) +{ + if (!cmd_ctx) + return ERROR_INVALID_ARGUMENTS; + + cmd_ctx->mode = mode; + return ERROR_OK; +} + /* sleep command sleeps for miliseconds * this is useful in target startup scripts */ @@ -566,3 +519,13 @@ int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **ar return ERROR_OK; } + +int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc) +{ + if (argc!=1) + return ERROR_COMMAND_SYNTAX_ERROR; + + fast_and_dangerous = strcmp("enable", args[0])==0; + + return ERROR_OK; +}