X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fhelper%2Fcommand.c;h=568596d17b96afe4c92bd30aef2f44eb8988ad2c;hb=c8267930c7cff5685b33cd0174deb75a46dbb09b;hp=3f439426a1764fba9e977cef3fcc176e6729f04d;hpb=fd343bea7f11796a9fba77158fe84b0ccaac1a4b;p=fw%2Fopenocd diff --git a/src/helper/command.c b/src/helper/command.c index 3f439426a..568596d17 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) @@ -139,7 +158,7 @@ static const char **script_command_args_alloc( return words; } -static struct command_context *current_command_context(void) +static 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"); @@ -164,15 +183,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); @@ -253,19 +271,44 @@ static struct command **command_list_for_parent( return parent ? &parent->children : &cmd_ctx->commands; } +static void command_free(struct command *c) +{ + /// @todo if command has a handler, unregister its jim command! + + while (NULL != c->children) + { + struct command *tmp = c->children; + c->children = tmp->next; + command_free(tmp); + } + + if (c->name) + free(c->name); + if (c->help) + free((void*)c->help); + if (c->usage) + free((void*)c->usage); + free(c); +} + static struct command *command_new(struct command_context *cmd_ctx, struct command *parent, const struct command_registration *cr) { assert(cr->name); - struct command *c = malloc(sizeof(struct command)); - memset(c, 0, sizeof(struct command)); + struct command *c = calloc(1, sizeof(struct command)); + if (NULL == c) + return NULL; c->name = strdup(cr->name); if (cr->help) c->help = strdup(cr->help); if (cr->usage) c->usage = strdup(cr->usage); + + if (!c->name || (cr->help && !c->help) || (cr->usage && !c->usage)) + goto command_new_error; + c->parent = parent; c->handler = cr->handler; c->jim_handler = cr->jim_handler; @@ -275,31 +318,18 @@ static struct command *command_new(struct command_context *cmd_ctx, command_add_child(command_list_for_parent(cmd_ctx, parent), c); return c; -} -static void command_free(struct command *c) -{ - /// @todo if command has a handler, unregister its jim command! - while (NULL != c->children) - { - struct command *tmp = c->children; - c->children = tmp->next; - command_free(tmp); - } - - if (c->name) - free(c->name); - if (c->help) - free((void*)c->help); - if (c->usage) - free((void*)c->usage); - free(c); +command_new_error: + command_free(c); + return NULL; } 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; @@ -348,11 +378,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) { @@ -535,13 +565,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, @@ -552,7 +591,7 @@ static int run_command(struct command_context *context, /* Print help for command */ char *full_name = command_name(c, ' '); if (NULL != full_name) { - command_run_linef(context, "help %s", full_name); + command_run_linef(context, "usage %s", full_name); free(full_name); } else retval = -ENOMEM; @@ -586,6 +625,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) @@ -673,12 +713,12 @@ struct command_context* copy_command_context(struct command_context* context) return copy_context; } -int command_done(struct command_context *context) +void command_done(struct command_context *cmd_ctx) { - free(context); - context = NULL; + if (NULL == cmd_ctx) + return; - return ERROR_OK; + free(cmd_ctx); } /* find full path to file */ @@ -794,12 +834,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; } @@ -821,13 +861,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; } @@ -860,38 +900,117 @@ 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) { - command_help_show_indent(n); - LOG_USER_N("%s", command_name(c, ' ')); - if (c->usage) { - LOG_USER_N(" "); - command_help_show_wrap(c->usage, 0, n + 5); + if (!command_can_run(CMD_CTX, c)) + return ERROR_OK; + + char *cmd_name = command_name(c, ' '); + if (NULL == cmd_name) + return -ENOMEM; + + /* 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 (is_match) + { + command_help_show_indent(n); + LOG_USER_N("%s", cmd_name); } - else - LOG_USER_N("\n"); - if (show_help && c->help) - command_help_show_wrap(c->help, n + 3, n + 3); + free(cmd_name); + + if (is_match) + { + if (c->usage) { + LOG_USER_N(" "); + command_help_show_wrap(c->usage, 0, n + 5); + } + 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); + free(msg); + } else + return -ENOMEM; + } + if (++n >= 2) 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, @@ -909,6 +1028,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); @@ -921,7 +1041,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 @@ -943,10 +1063,10 @@ static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } else { - c = command_find(cmd_ctx->commands, "help"); + c = command_find(cmd_ctx->commands, "usage"); if (NULL == c) { - LOG_ERROR("unknown command, but help is missing too"); + LOG_ERROR("unknown command, but usage is missing too"); return JIM_ERR; } count = argc - remaining; @@ -965,8 +1085,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; @@ -998,7 +1119,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 @@ -1103,7 +1224,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) @@ -1143,19 +1264,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 }; @@ -1163,39 +1287,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", @@ -1206,7 +1334,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; @@ -1218,13 +1346,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: @@ -1236,7 +1370,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"; @@ -1244,8 +1378,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", @@ -1291,17 +1427,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 }