Fix usage/help search for subcommands.
[fw/openocd] / src / helper / command.c
index 319f0810d6169dbfe3ec9cef67279675cd0cf749..b4e31ea1a7e1d2a3a975ad5515999dcd1fd14306 100644 (file)
@@ -36,7 +36,7 @@
 #endif
 
 // @todo the inclusion of target.h here is a layering violation
-#include "target.h"
+#include <target/target.h>
 #include "command.h"
 #include "configuration.h"
 #include "log.h"
 /* 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);
@@ -308,8 +326,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;
@@ -358,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)
        {
@@ -545,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,
@@ -596,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)
@@ -804,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;
 }
@@ -831,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;
 }
 
@@ -870,7 +900,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,18 +909,30 @@ 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);
+       /* 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);
+       }
        free(cmd_name);
 
-       if (c->usage) {
-               LOG_USER_N(" ");
-               command_help_show_wrap(c->usage, 0, n + 5);
+       if (is_match)
+       {
+               if (c->usage) {
+                       LOG_USER_N(" ");
+                       command_help_show_wrap(c->usage, 0, n + 5);
+               }
+               else
+                       LOG_USER_N("\n");
        }
-       else
-               LOG_USER_N("\n");
 
-       if (show_help)
+       if (is_match && show_help)
        {
                const char *stage_msg;
                switch (c->mode) {
@@ -913,22 +955,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);
+       retval = CALL_COMMAND_HANDLER(command_help_show_list,
+                       c, 0, full, match);
 
-       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, full);
+       if (CMD_ARGC >= 1)
+               free(match);
+       return retval;
 }
 
 static int command_unknown_find(unsigned argc, Jim_Obj *const *argv,
@@ -946,6 +1016,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 +1029,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,7 +1073,7 @@ 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)
        {
@@ -1035,7 +1106,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
@@ -1243,7 +1314,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,12 +1326,17 @@ 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;
 
 #if defined(_MSC_VER)
        /* WinXX - is generic, the forward
@@ -1328,17 +1404,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
 }