X-Git-Url: https://review.openocd.org/gitweb?a=blobdiff_plain;f=src%2Fhelper%2Fcommand.c;h=b2aa76b0b9e0ba00e0236f94913e975e29f35464;hb=933b4579f06d25e349e6648ec4aff114e634164d;hp=a1ba358500324c17ef204558b86685422d66acc0;hpb=c297a14f7072d98bdad6bd7b9bac3a3069de9ed9;p=openocd.git diff --git a/src/helper/command.c b/src/helper/command.c index a1ba358500..b2aa76b0b9 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -44,6 +44,9 @@ #include "jim-eventloop.h" +/* nice short description of source file */ +#define __THIS__FILE__ "command.c" + Jim_Interp *interp = NULL; static int run_command(struct command_context *context, @@ -56,6 +59,34 @@ static void tcl_output(void *privData, const char *file, unsigned line, Jim_AppendString(interp, tclOutput, string, strlen(string)); } +static Jim_Obj *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; + Jim_IncrRefCount(tclOutput); + log_add_callback(tcl_output, tclOutput); + return tclOutput; +} + +static void command_log_capture_finish(Jim_Interp *interp, Jim_Obj *tclOutput) +{ + log_remove_callback(tcl_output, tclOutput); + Jim_SetResult(interp, tclOutput); + Jim_DecrRefCount(interp, tclOutput); +} + +static int command_retval_set(Jim_Interp *interp, int retval) +{ + int *return_retval = Jim_GetAssocData(interp, "retval"); + if (return_retval != NULL) + *return_retval = retval; + + return (retval == ERROR_OK) ? JIM_OK : JIM_ERR; +} + extern struct command_context *global_cmd_ctx; void script_debug(Jim_Interp *interp, const char *name, @@ -122,62 +153,47 @@ static struct command_context *current_command_context(void) return cmd_ctx; } -static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +static int script_command_run(Jim_Interp *interp, + int argc, Jim_Obj *const *argv, struct command *c, bool capture) { - /* the private data is stashed in the interp structure */ - struct command *c; - int retval; - - /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might - * get overwritten by running other Jim commands! Treat it as an - * emphemeral global variable that is used in lieu of an argument - * to the fn and fish it out manually. - */ - c = interp->cmdPrivData; - if (c == NULL) - { - LOG_ERROR("BUG: interp->cmdPrivData == NULL"); - return JIM_ERR; - } target_call_timer_callbacks_now(); LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ - script_debug(interp, c->name, argc, argv); - unsigned nwords; const char **words = script_command_args_alloc(argc, argv, &nwords); if (NULL == words) return JIM_ERR; - /* capture log output and return it */ - Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0); - /* a garbage collect can happen, so we need a reference count to this object */ - Jim_IncrRefCount(tclOutput); - - log_add_callback(tcl_output, tclOutput); + Jim_Obj *tclOutput = NULL; + if (capture) + tclOutput = command_log_capture_start(interp); struct command_context *cmd_ctx = current_command_context(); - retval = run_command(cmd_ctx, c, (const char **)words, nwords); + int retval = run_command(cmd_ctx, c, (const char **)words, nwords); - log_remove_callback(tcl_output, tclOutput); - - /* We dump output into this local variable */ - Jim_SetResult(interp, tclOutput); - Jim_DecrRefCount(interp, tclOutput); + if (capture) + command_log_capture_finish(interp, tclOutput); script_command_args_free(words, nwords); + return command_retval_set(interp, retval); +} - int *return_retval = Jim_GetAssocData(interp, "retval"); - if (return_retval != NULL) - { - *return_retval = retval; - } +static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* the private data is stashed in the interp structure */ - return (retval == ERROR_OK)?JIM_OK:JIM_ERR; + struct command *c = interp->cmdPrivData; + assert(c); + script_debug(interp, c->name, argc, argv); + return script_command_run(interp, argc, argv, c, true); } -/* nice short description of source file */ -#define __THIS__FILE__ "command.c" +static struct command *command_root(struct command *c) +{ + while (NULL != c->parent) + c = c->parent; + return c; +} /** * Find a command by name from a list of commands. @@ -280,35 +296,32 @@ static void command_free(struct command *c) free(c); } +static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + static int register_command_handler(struct command *c) { - int retval = -ENOMEM; - const char *full_name = command_name(c, '_'); - if (NULL == full_name) - return retval; + const char *ocd_name = alloc_printf("ocd_%s", c->name); + if (NULL == ocd_name) + return JIM_ERR; - const char *ocd_name = alloc_printf("ocd_%s", full_name); - if (NULL == full_name) - goto free_full_name; + LOG_DEBUG("registering '%s'...", ocd_name); - Jim_CreateCommand(interp, ocd_name, script_command, c, NULL); + Jim_CmdProc func = c->handler ? &script_command : &command_unknown; + int retval = Jim_CreateCommand(interp, ocd_name, func, c, NULL); free((void *)ocd_name); + if (JIM_OK != retval) + return retval; /* we now need to add an overrideable proc */ - const char *override_name = alloc_printf("proc %s {args} {" - "if {[catch {eval ocd_%s $args}] == 0} " - "{return \"\"} else {return -code error}}", - full_name, full_name); - if (NULL == full_name) - goto free_full_name; - - Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__); - free((void *)override_name); + const char *override_name = alloc_printf( + "proc %s {args} {eval ocd_bouncer %s $args}", + c->name, c->name); + if (NULL == override_name) + return JIM_ERR; - retval = ERROR_OK; + retval = Jim_Eval_Named(interp, override_name, __FILE__, __LINE__); + free((void *)override_name); -free_full_name: - free((void *)full_name); return retval; } @@ -332,19 +345,20 @@ struct command* register_command(struct command_context *context, if (NULL == c) return NULL; - if (NULL != c->handler) + int retval = ERROR_OK; + if (NULL != cr->jim_handler && NULL == parent) { - int retval = register_command_handler(c); - if (ERROR_OK != retval) - { - unregister_command(context, parent, name); - return NULL; - } + retval = Jim_CreateCommand(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)); - if (NULL != cr->jim_handler && NULL == parent) - Jim_CreateCommand(interp, cr->name, cr->jim_handler, cr->jim_handler_data, NULL); - + if (ERROR_OK != retval) + { + unregister_command(context, parent, name); + c = NULL; + } return c; } @@ -425,6 +439,14 @@ int unregister_command(struct command_context *context, return ERROR_OK; } +void command_set_handler_data(struct command *c, void *p) +{ + if (NULL != c->handler || NULL != c->jim_handler) + c->jim_handler_data = p; + for (struct command *cc = c->children; NULL != cc; cc = cc->next) + command_set_handler_data(cc, p); +} + void command_output_text(struct command_context *context, const char *data) { if (context && context->output_handler && data) { @@ -766,23 +788,13 @@ static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2) return JIM_ERR; - int retcode; - const char *str = Jim_GetString(argv[1], NULL); - - /* capture log output and return it */ - Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0); - /* a garbage collect can happen, so we need a reference count to this object */ - Jim_IncrRefCount(tclOutput); - log_add_callback(tcl_output, tclOutput); + Jim_Obj *tclOutput = command_log_capture_start(interp); - retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__); + const char *str = Jim_GetString(argv[1], NULL); + int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__); - log_remove_callback(tcl_output, tclOutput); - - /* We dump output into this local variable */ - Jim_SetResult(interp, tclOutput); - Jim_DecrRefCount(interp, tclOutput); + command_log_capture_finish(interp, tclOutput); return retcode; } @@ -793,6 +805,8 @@ static COMMAND_HELPER(command_help_find, struct command *head, if (0 == CMD_ARGC) return ERROR_INVALID_ARGUMENTS; *out = command_find(head, CMD_ARGV[0]); + if (NULL == *out && strncmp(CMD_ARGV[0], "ocd_", 4) == 0) + *out = command_find(head, CMD_ARGV[0] + 4); if (NULL == *out) return ERROR_INVALID_ARGUMENTS; if (--CMD_ARGC == 0) @@ -861,56 +875,54 @@ static COMMAND_HELPER(command_help_show, struct command *c, unsigned n, } COMMAND_HANDLER(handle_help_command) { - struct command *c = CMD_CTX->commands; + bool full = strcmp(CMD_NAME, "help") == 0; - if (0 == CMD_ARGC) - return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, true); - - int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c); - if (ERROR_OK != retval) - return retval; - - return CALL_COMMAND_HANDLER(command_help_show, c, 0, true); -} - -COMMAND_HANDLER(handle_usage_command) -{ struct command *c = CMD_CTX->commands; if (0 == CMD_ARGC) - return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, false); + 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; - return CALL_COMMAND_HANDLER(command_help_show, c, 0, false); + return CALL_COMMAND_HANDLER(command_help_show, c, 0, full); } static int command_unknown_find(unsigned argc, Jim_Obj *const *argv, - struct command *head, struct command **out) + struct command *head, struct command **out, bool top_level) { if (0 == argc) return argc; - struct command *c = command_find(head, Jim_GetString(argv[0], NULL)); + const char *cmd_name = Jim_GetString(argv[0], NULL); + struct command *c = command_find(head, cmd_name); + if (NULL == c && top_level && strncmp(cmd_name, "ocd_", 4) == 0) + c = command_find(head, cmd_name + 4); if (NULL == c) return argc; *out = c; - return command_unknown_find(--argc, ++argv, (*out)->children, out); + 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); - script_debug(interp, cmd_name, argc - 1, argv + 1); + if (strcmp(cmd_name, "unknown") == 0) + { + if (argc == 1) + return JIM_OK; + argc--; + argv++; + } + script_debug(interp, cmd_name, argc, argv); struct command_context *cmd_ctx = current_command_context(); struct command *c = cmd_ctx->commands; - int remaining = command_unknown_find(argc - 1, argv + 1, c, &c); + int remaining = command_unknown_find(argc, argv, c, &c, true); // if nothing could be consumed, then it's really an unknown command - if (remaining == argc - 1) + if (remaining == argc) { - const char *cmd = Jim_GetString(argv[1], NULL); + const char *cmd = Jim_GetString(argv[0], NULL); LOG_ERROR("Unknown command:\n %s", cmd); return JIM_OK; } @@ -943,19 +955,62 @@ static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return (*c->jim_handler)(interp, count, start); } - unsigned nwords; - const char **words = script_command_args_alloc(count, start, &nwords); - if (NULL == words) - return JIM_ERR; + return script_command_run(interp, count, start, c, found); +} + +static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct command_context *cmd_ctx = current_command_context(); + enum command_mode mode; + if (argc > 1) + { + 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 + if (remaining == argc - 1) + { + Jim_SetResultString(interp, "unknown", -1); + return JIM_OK; + } + mode = c->mode; + } + else + mode = cmd_ctx->mode; + + const char *mode_str; + switch (mode) { + case COMMAND_ANY: mode_str = "any"; break; + case COMMAND_CONFIG: mode_str = "config"; break; + case COMMAND_EXEC: mode_str = "exec"; break; + default: mode_str = "unknown"; break; + } + Jim_SetResultString(interp, mode_str, -1); + return JIM_OK; +} - int retval = run_command(cmd_ctx, c, words, nwords); +static int jim_command_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (1 == argc) + return JIM_ERR; - script_command_args_free(words, nwords); + struct command_context *cmd_ctx = current_command_context(); + 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 + if (remaining == argc - 1) + { + Jim_SetResultString(interp, "unknown", -1); + return JIM_OK; + } - if (!found && ERROR_OK == retval) - retval = ERROR_FAIL; + if (c->jim_handler) + Jim_SetResultString(interp, "native", -1); + else if (c->handler) + Jim_SetResultString(interp, "simple", -1); + else + Jim_SetResultString(interp, "group", -1); - return retval; + return JIM_OK; } int help_add_command(struct command_context *cmd_ctx, struct command *parent, @@ -1079,6 +1134,27 @@ COMMAND_HANDLER(handle_sleep_command) return ERROR_OK; } +static const struct command_registration command_subcommand_handlers[] = { + { + .name = "mode", + .mode = COMMAND_ANY, + .jim_handler = &jim_command_mode, + .usage = "[ ...]", + .help = "Returns the command modes allowed by a command:" + "'any', 'config', or 'exec'. If no command is" + "specified, returns the current command mode.", + }, + { + .name = "type", + .mode = COMMAND_ANY, + .jim_handler = &jim_command_type, + .usage = " ...", + .help = "Returns the type of built-in command:" + "'native', 'simple', 'group', or 'unknown'", + }, + COMMAND_REGISTRATION_DONE +}; + static const struct command_registration command_builtin_handlers[] = { { .name = "add_help_text", @@ -1106,15 +1182,21 @@ static const struct command_registration command_builtin_handlers[] = { .name = "help", .handler = &handle_help_command, .mode = COMMAND_ANY, - .help = "show built-in command help", - .usage = "[ ...]", + .help = "show full command help", + .usage = "[ ...]", }, { .name = "usage", - .handler = &handle_usage_command, + .handler = &handle_help_command, .mode = COMMAND_ANY, - .help = "show command usage", - .usage = "[ ...]", + .help = "show basic command usage", + .usage = "[ ...]", + }, + { + .name = "command", + .mode= COMMAND_ANY, + .help = "core command group (introspection)", + .chain = command_subcommand_handlers, }, COMMAND_REGISTRATION_DONE }; @@ -1164,7 +1246,6 @@ struct command_context* command_init(const char *startup_tcl) Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS", Jim_NewStringObj(interp, HostOs , strlen(HostOs))); - Jim_CreateCommand(interp, "unknown", &command_unknown, NULL, NULL); Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL); Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL); Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);