X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fhelper%2Fcommand.c;h=ea768b2d16bd21d34112dc08a57f7dad214599fd;hp=319f0810d6169dbfe3ec9cef67279675cd0cf749;hb=127f9da4fe0a3e2eaa86c5cd32bb45b6a7083171;hpb=5dd6457d2c06caed01d4b75ecf1415afc68b48ea diff --git a/src/helper/command.c b/src/helper/command.c index 319f0810d6..ea768b2d16 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -36,7 +36,7 @@ #endif // @todo the inclusion of target.h here is a layering violation -#include "target.h" +#include #include "command.h" #include "configuration.h" #include "log.h" @@ -47,35 +47,54 @@ /* nice short description of source file */ #define __THIS__FILE__ "command.c" -Jim_Interp *interp = NULL; static int run_command(struct command_context *context, struct command *c, const char *words[], unsigned num_words); +struct log_capture_state { + Jim_Interp *interp; + Jim_Obj *output; +}; + static void tcl_output(void *privData, const char *file, unsigned line, const char *function, const char *string) { - Jim_Obj *tclOutput = (Jim_Obj *)privData; - Jim_AppendString(interp, tclOutput, string, strlen(string)); + struct log_capture_state *state = (struct log_capture_state *)privData; + Jim_AppendString(state->interp, state->output, string, strlen(string)); } -static Jim_Obj *command_log_capture_start(Jim_Interp *interp) +static struct log_capture_state *command_log_capture_start(Jim_Interp *interp) { /* capture log output and return it. A garbage collect can * happen, so we need a reference count to this object */ Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0); if (NULL == tclOutput) return NULL; + + struct log_capture_state *state = malloc(sizeof(*state)); + if (NULL == state) + return NULL; + + state->interp = interp; Jim_IncrRefCount(tclOutput); - log_add_callback(tcl_output, tclOutput); - return tclOutput; + state->output = tclOutput; + + log_add_callback(tcl_output, state); + + return state; } -static void command_log_capture_finish(Jim_Interp *interp, Jim_Obj *tclOutput) +static void command_log_capture_finish(struct log_capture_state *state) { - log_remove_callback(tcl_output, tclOutput); - Jim_SetResult(interp, tclOutput); - Jim_DecrRefCount(interp, tclOutput); + if (NULL == state) + return; + + log_remove_callback(tcl_output, state); + + Jim_SetResult(state->interp, state->output); + Jim_DecrRefCount(state->interp, state->output); + + free(state); } static int command_retval_set(Jim_Interp *interp, int retval) @@ -89,10 +108,15 @@ static int command_retval_set(Jim_Interp *interp, int retval) extern struct command_context *global_cmd_ctx; +/* dump a single line to the log for the command. + * Do nothing in case we are not at debug level 3 */ void script_debug(Jim_Interp *interp, const char *name, unsigned argc, Jim_Obj *const *argv) { - LOG_DEBUG("command - %s", name); + if (debug_level < LOG_LVL_DEBUG) + return; + + char * dbg = alloc_printf("command - %s", name); for (unsigned i = 0; i < argc; i++) { int len; @@ -102,8 +126,12 @@ void script_debug(Jim_Interp *interp, const char *name, if (*w == '#') break; - LOG_DEBUG("%s - argv[%d]=%s", name, i, w); + char * t = alloc_printf("%s %s", dbg, w); + free (dbg); + dbg = t; } + LOG_DEBUG("%s", dbg); + free(dbg); } static void script_command_args_free(const char **words, unsigned nwords) @@ -139,14 +167,18 @@ static const char **script_command_args_alloc( return words; } -static struct command_context *current_command_context(void) +struct command_context *current_command_context(Jim_Interp *interp) { /* grab the command context from the associated data */ struct command_context *cmd_ctx = Jim_GetAssocData(interp, "context"); if (NULL == cmd_ctx) { /* Tcl can invoke commands directly instead of via command_run_line(). This would - * happen when the Jim Tcl interpreter is provided by eCos. + * happen when the Jim Tcl interpreter is provided by eCos or if we are running + * commands in a startup script. + * + * A telnet or gdb server would provide a non-default command context to + * handle piping of error output, have a separate current target, etc. */ cmd_ctx = global_cmd_ctx; } @@ -164,15 +196,14 @@ static int script_command_run(Jim_Interp *interp, if (NULL == words) return JIM_ERR; - Jim_Obj *tclOutput = NULL; + struct log_capture_state *state = NULL; if (capture) - tclOutput = command_log_capture_start(interp); + state = command_log_capture_start(interp); - struct command_context *cmd_ctx = current_command_context(); + struct command_context *cmd_ctx = current_command_context(interp); int retval = run_command(cmd_ctx, c, (const char **)words, nwords); - if (capture) - command_log_capture_finish(interp, tclOutput); + command_log_capture_finish(state); script_command_args_free(words, nwords); return command_retval_set(interp, retval); @@ -308,8 +339,10 @@ command_new_error: static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv); -static int register_command_handler(struct command *c) +static int register_command_handler(struct command_context *cmd_ctx, + struct command *c) { + Jim_Interp *interp = cmd_ctx->interp; const char *ocd_name = alloc_printf("ocd_%s", c->name); if (NULL == ocd_name) return JIM_ERR; @@ -329,7 +362,7 @@ static int register_command_handler(struct command *c) if (NULL == override_name) return JIM_ERR; - retval = Jim_Eval_Named(interp, override_name, __FILE__, __LINE__); + retval = Jim_Eval_Named(interp, override_name, 0, 0); free((void *)override_name); return retval; @@ -346,7 +379,10 @@ struct command* register_command(struct command_context *context, struct command *c = command_find(*head, name); if (NULL != c) { - LOG_ERROR("command '%s' is already registered in '%s' context", + /* TODO: originally we treated attempting to register a cmd twice as an error + * Sometimes we need this behaviour, such as with flash banks. + * http://www.mail-archive.com/openocd-development@lists.berlios.de/msg11152.html */ + LOG_DEBUG("command '%s' is already registered in '%s' context", name, parent ? parent->name : ""); return c; } @@ -358,11 +394,11 @@ struct command* register_command(struct command_context *context, int retval = ERROR_OK; if (NULL != cr->jim_handler && NULL == parent) { - retval = Jim_CreateCommand(interp, cr->name, + retval = Jim_CreateCommand(context->interp, cr->name, cr->jim_handler, cr->jim_handler_data, NULL); } else if (NULL != cr->handler || NULL != parent) - retval = register_command_handler(command_root(c)); + retval = register_command_handler(context, command_root(c)); if (ERROR_OK != retval) { @@ -545,13 +581,22 @@ static int run_command(struct command_context *context, { if (!command_can_run(context, c)) { - /* Config commands can not run after the config stage */ - LOG_ERROR("The '%s' command must be used before 'init'.", c->name); + /* Many commands may be run only before/after 'init' */ + const char *when; + switch (c->mode) { + case COMMAND_CONFIG: when = "before"; break; + case COMMAND_EXEC: when = "after"; break; + // handle the impossible with humor; it guarantees a bug report! + default: when = "if Cthulhu is summoned by"; break; + } + LOG_ERROR("The '%s' command must be used %s 'init'.", + c->name, when); return ERROR_FAIL; } struct command_invocation cmd = { .ctx = context, + .current = c, .name = c->name, .argc = num_words - 1, .argv = words + 1, @@ -596,6 +641,7 @@ int command_run_line(struct command_context *context, char *line) * happen when the Jim Tcl interpreter is provided by eCos for * instance. */ + Jim_Interp *interp = context->interp; Jim_DeleteAssocData(interp, "context"); retcode = Jim_SetAssocData(interp, "context", NULL, context); if (retcode == JIM_OK) @@ -605,7 +651,7 @@ int command_run_line(struct command_context *context, char *line) retcode = Jim_SetAssocData(interp, "retval", NULL, &retval); if (retcode == JIM_OK) { - retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__); + retcode = Jim_Eval_Named(interp, line, 0, 0); Jim_DeleteAssocData(interp, "retval"); } @@ -804,12 +850,12 @@ static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv) if (argc != 2) return JIM_ERR; - Jim_Obj *tclOutput = command_log_capture_start(interp); + struct log_capture_state *state = command_log_capture_start(interp); const char *str = Jim_GetString(argv[1], NULL); int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__); - command_log_capture_finish(interp, tclOutput); + command_log_capture_finish(state); return retcode; } @@ -831,13 +877,13 @@ static COMMAND_HELPER(command_help_find, struct command *head, } static COMMAND_HELPER(command_help_show, struct command *c, unsigned n, - bool show_help); + bool show_help, const char *match); static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n, - bool show_help) + bool show_help, const char *match) { for (struct command *c = head; NULL != c; c = c->next) - CALL_COMMAND_HANDLER(command_help_show, c, n, show_help); + CALL_COMMAND_HANDLER(command_help_show, c, n, show_help, match); return ERROR_OK; } @@ -870,7 +916,7 @@ static void command_help_show_wrap(const char *str, unsigned n, unsigned n2) } } static COMMAND_HELPER(command_help_show, struct command *c, unsigned n, - bool show_help) + bool show_help, const char *match) { if (!command_can_run(CMD_CTX, c)) return ERROR_OK; @@ -879,28 +925,52 @@ static COMMAND_HELPER(command_help_show, struct command *c, unsigned n, if (NULL == cmd_name) return -ENOMEM; - command_help_show_indent(n); - LOG_USER_N("%s", cmd_name); - free(cmd_name); + /* If the match string occurs anywhere, we print out + * stuff for this command. */ + bool is_match = (strstr(cmd_name, match) != NULL) || + ((c->usage != NULL) && (strstr(c->usage, match) != NULL)) || + ((c->help != NULL) && (strstr(c->help, match) != NULL)); - if (c->usage) { - LOG_USER_N(" "); - command_help_show_wrap(c->usage, 0, n + 5); + if (is_match) + { + command_help_show_indent(n); + LOG_USER_N("%s", cmd_name); } - else - LOG_USER_N("\n"); + free(cmd_name); - if (show_help) + if (is_match) { - const char *stage_msg; - switch (c->mode) { - case COMMAND_CONFIG: stage_msg = "CONFIG"; break; - case COMMAND_EXEC: stage_msg = "EXEC"; break; - case COMMAND_ANY: stage_msg = "CONFIG or EXEC"; break; - default: stage_msg = "***UNKNOWN***"; break; + if (c->usage) { + LOG_USER_N(" "); + command_help_show_wrap(c->usage, 0, n + 5); } - char *msg = alloc_printf("%s%sValid Modes: %s", - c->help ? : "", c->help ? " " : "", stage_msg); + else + LOG_USER_N("\n"); + } + + if (is_match && show_help) + { + char *msg; + + /* Normal commands are runtime-only; highlight exceptions */ + if (c->mode != COMMAND_EXEC) { + const char *stage_msg = ""; + + switch (c->mode) { + case COMMAND_CONFIG: + stage_msg = " (configuration command)"; + break; + case COMMAND_ANY: + stage_msg = " (command valid any time)"; + break; + default: + stage_msg = " (?mode error?)"; + break; + } + msg = alloc_printf("%s%s", c->help ? : "", stage_msg); + } else + msg = alloc_printf("%s", c->help ? : ""); + if (NULL != msg) { command_help_show_wrap(msg, n + 3, n + 3); @@ -913,22 +983,50 @@ static COMMAND_HELPER(command_help_show, struct command *c, unsigned n, return ERROR_OK; return CALL_COMMAND_HANDLER(command_help_show_list, - c->children, n, show_help); + c->children, n, show_help, match); } COMMAND_HANDLER(handle_help_command) { bool full = strcmp(CMD_NAME, "help") == 0; - + int retval; struct command *c = CMD_CTX->commands; + char *match = NULL; + + if (CMD_ARGC == 0) + match = ""; + else if (CMD_ARGC >= 1) { + unsigned i; + + for (i = 0; i < CMD_ARGC; ++i) { + if (NULL != match) { + char *prev = match; + + match = alloc_printf("%s %s", match, + CMD_ARGV[i]); + free(prev); + if (NULL == match) { + LOG_ERROR("unable to build " + "search string"); + return -ENOMEM; + } + } else { + match = alloc_printf("%s", CMD_ARGV[i]); + if (NULL == match) { + LOG_ERROR("unable to build " + "search string"); + return -ENOMEM; + } + } + } + } else + return ERROR_COMMAND_SYNTAX_ERROR; - if (0 == CMD_ARGC) - return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, full); - - int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c); - if (ERROR_OK != retval) - return retval; + retval = CALL_COMMAND_HANDLER(command_help_show_list, + c, 0, full, match); - return CALL_COMMAND_HANDLER(command_help_show, c, 0, full); + if (CMD_ARGC >= 1) + free(match); + return retval; } static int command_unknown_find(unsigned argc, Jim_Obj *const *argv, @@ -946,6 +1044,7 @@ static int command_unknown_find(unsigned argc, Jim_Obj *const *argv, return command_unknown_find(--argc, ++argv, (*out)->children, out, false); } + static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *cmd_name = Jim_GetString(argv[0], NULL); @@ -958,7 +1057,7 @@ static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } script_debug(interp, cmd_name, argc, argv); - struct command_context *cmd_ctx = current_command_context(); + struct command_context *cmd_ctx = current_command_context(interp); struct command *c = cmd_ctx->commands; int remaining = command_unknown_find(argc, argv, c, &c, true); // if nothing could be consumed, then it's really an unknown command @@ -1002,8 +1101,9 @@ static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - struct command_context *cmd_ctx = current_command_context(); + struct command_context *cmd_ctx = current_command_context(interp); enum command_mode mode; + if (argc > 1) { struct command *c = cmd_ctx->commands; @@ -1035,7 +1135,7 @@ static int jim_command_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv) if (1 == argc) return JIM_ERR; - struct command_context *cmd_ctx = current_command_context(); + struct command_context *cmd_ctx = current_command_context(interp); struct command *c = cmd_ctx->commands; int remaining = command_unknown_find(argc - 1, argv + 1, c, &c, true); // if nothing could be consumed, then it's an unknown command @@ -1140,7 +1240,7 @@ COMMAND_HANDLER(handle_help_add_command) return help_add_command(CMD_CTX, c, cmd_name, help, usage); } -/* sleep command sleeps for miliseconds +/* sleep command sleeps for milliseconds * this is useful in target startup scripts */ COMMAND_HANDLER(handle_sleep_command) @@ -1180,19 +1280,22 @@ static const struct command_registration command_subcommand_handlers[] = { { .name = "mode", .mode = COMMAND_ANY, - .jim_handler = &jim_command_mode, - .usage = "[ ...]", + .jim_handler = jim_command_mode, + .usage = "[command_name ...]", .help = "Returns the command modes allowed by a command:" "'any', 'config', or 'exec'. If no command is" - "specified, returns the current command mode.", + "specified, returns the current command mode. " + "Returns 'unknown' if an unknown command is given. " + "Command can be multiple tokens.", }, { .name = "type", .mode = COMMAND_ANY, - .jim_handler = &jim_command_type, - .usage = " ...", + .jim_handler = jim_command_type, + .usage = "command_name [...]", .help = "Returns the type of built-in command:" - "'native', 'simple', 'group', or 'unknown'", + "'native', 'simple', 'group', or 'unknown'. " + "Command can be multiple tokens.", }, COMMAND_REGISTRATION_DONE }; @@ -1200,39 +1303,43 @@ static const struct command_registration command_subcommand_handlers[] = { static const struct command_registration command_builtin_handlers[] = { { .name = "add_help_text", - .handler = &handle_help_add_command, + .handler = handle_help_add_command, .mode = COMMAND_ANY, - .help = "add new command help text", - .usage = " [...] ]", + .help = "Add new command help text; " + "Command can be multiple tokens.", + .usage = "command_name helptext_string", }, { .name = "add_usage_text", - .handler = &handle_help_add_command, + .handler = handle_help_add_command, .mode = COMMAND_ANY, - .help = "add new command usage text", - .usage = " [...] ]", + .help = "Add new command usage text; " + "command can be multiple tokens.", + .usage = "command_name usage_string", }, { .name = "sleep", - .handler = &handle_sleep_command, + .handler = handle_sleep_command, .mode = COMMAND_ANY, - .help = "sleep for n milliseconds. " - "\"busy\" will busy wait", - .usage = " [busy]", + .help = "Sleep for specified number of milliseconds. " + "\"busy\" will busy wait instead (avoid this).", + .usage = "milliseconds ['busy']", }, { .name = "help", - .handler = &handle_help_command, + .handler = handle_help_command, .mode = COMMAND_ANY, - .help = "show full command help", - .usage = "[ ...]", + .help = "Show full command help; " + "command can be multiple tokens.", + .usage = "[command_name]", }, { .name = "usage", - .handler = &handle_help_command, + .handler = handle_help_command, .mode = COMMAND_ANY, - .help = "show basic command usage", - .usage = "[ ...]", + .help = "Show basic command usage; " + "command can be multiple tokens.", + .usage = "[command_name]", }, { .name = "command", @@ -1243,7 +1350,7 @@ static const struct command_registration command_builtin_handlers[] = { COMMAND_REGISTRATION_DONE }; -struct command_context* command_init(const char *startup_tcl) +struct command_context* command_init(const char *startup_tcl, Jim_Interp *interp) { struct command_context* context = malloc(sizeof(struct command_context)); const char *HostOs; @@ -1255,13 +1362,19 @@ struct command_context* command_init(const char *startup_tcl) context->output_handler_priv = NULL; #if !BUILD_ECOSBOARD - Jim_InitEmbedded(); - /* Create an interpreter */ - interp = Jim_CreateInterp(); - /* Add all the Jim core commands */ - Jim_RegisterCoreCommands(interp); + /* Create a jim interpreter if we were not handed one */ + if (interp == NULL) + { + Jim_InitEmbedded(); + /* Create an interpreter */ + interp = Jim_CreateInterp(); + /* Add all the Jim core commands */ + Jim_RegisterCoreCommands(interp); + } #endif + context->interp = interp; + /* Stick to lowercase for HostOS strings. */ #if defined(_MSC_VER) /* WinXX - is generic, the forward * looking problem is this: @@ -1273,7 +1386,7 @@ struct command_context* command_init(const char *startup_tcl) HostOs = "winxx"; #elif defined(__linux__) HostOs = "linux"; -#elif defined(__DARWIN__) +#elif defined(__APPLE__) || defined(__DARWIN__) HostOs = "darwin"; #elif defined(__CYGWIN__) HostOs = "cygwin"; @@ -1281,8 +1394,10 @@ struct command_context* command_init(const char *startup_tcl) HostOs = "mingw32"; #elif defined(__ECOS) HostOs = "ecos"; +#elif defined(__FreeBSD__) + HostOs = "freebsd"; #else -#warn unrecognized host OS... +#warning "Unrecognized host OS..." HostOs = "other"; #endif Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS", @@ -1328,17 +1443,16 @@ int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode return ERROR_OK; } -void process_jim_events(void) +void process_jim_events(struct command_context *cmd_ctx) { #if !BUILD_ECOSBOARD static int recursion = 0; + if (recursion) + return; - if (!recursion) - { - recursion++; - Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT); - recursion--; - } + recursion++; + Jim_ProcessEvents(cmd_ctx->interp, JIM_ALL_EVENTS | JIM_DONT_WAIT); + recursion--; #endif }