add jim_handler to command_registration
[fw/openocd] / src / helper / command.c
index df4667b92a7dafc7f66e9a0830979d2bc040fb1d..3cb36ea27590850c4f18131f10d265604b6af7d4 100644 (file)
@@ -108,11 +108,24 @@ static const char **script_command_args_alloc(
        return words;
 }
 
+static struct command_context *current_command_context(void)
+{
+       /* 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.
+                */
+               cmd_ctx = global_cmd_ctx;
+       }
+       return cmd_ctx;
+}
+
 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
        /* the private data is stashed in the interp structure */
        struct command *c;
-       struct command_context *context;
        int retval;
 
        /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
@@ -136,16 +149,6 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        if (NULL == words)
                return JIM_ERR;
 
-       /* grab the command context from the associated data */
-       context = Jim_GetAssocData(interp, "context");
-       if (context == NULL)
-       {
-               /* Tcl can invoke commands directly instead of via command_run_line(). This would
-                * happen when the Jim Tcl interpreter is provided by eCos.
-                */
-               context = global_cmd_ctx;
-       }
-
        /* 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 */
@@ -153,7 +156,8 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
        log_add_callback(tcl_output, tclOutput);
 
-       retval = run_command(context, c, (const char **)words, nwords);
+       struct command_context *cmd_ctx = current_command_context();
+       retval = run_command(cmd_ctx, c, (const char **)words, nwords);
 
        log_remove_callback(tcl_output, tclOutput);
 
@@ -189,6 +193,16 @@ static struct command *command_find(struct command *head, const char *name)
        }
        return NULL;
 }
+struct command *command_find_in_context(struct command_context *cmd_ctx,
+               const char *name)
+{
+       return command_find(cmd_ctx->commands, name);
+}
+struct command *command_find_in_parent(struct command *parent,
+               const char *name)
+{
+       return command_find(parent->children, name);
+}
 
 /**
  * Add the command into the linked list, sorted by name.
@@ -224,23 +238,23 @@ static struct command **command_list_for_parent(
 }
 
 static struct command *command_new(struct command_context *cmd_ctx,
-               struct command *parent, const char *name,
-               command_handler_t handler, enum command_mode mode,
-               const char *help, const char *usage)
+               struct command *parent, const struct command_registration *cr)
 {
-       assert(name);
+       assert(cr->name);
 
        struct command *c = malloc(sizeof(struct command));
        memset(c, 0, sizeof(struct command));
 
-       c->name = strdup(name);
-       if (help)
-               c->help = strdup(help);
-       if (usage)
-               c->usage = strdup(usage);
+       c->name = strdup(cr->name);
+       if (cr->help)
+               c->help = strdup(cr->help);
+       if (cr->usage)
+               c->usage = strdup(cr->usage);
        c->parent = parent;
-       c->handler = handler;
-       c->mode = mode;
+       c->handler = cr->handler;
+       c->jim_handler = cr->jim_handler;
+       c->jim_handler_data = cr->jim_handler_data;
+       c->mode = cr->mode;
 
        command_add_child(command_list_for_parent(cmd_ctx, parent), c);
 
@@ -266,6 +280,38 @@ static void command_free(struct command *c)
        free(c);
 }
 
+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", full_name);
+       if (NULL == full_name)
+               goto free_full_name;
+
+       Jim_CreateCommand(interp, ocd_name, script_command, c, NULL);
+       free((void *)ocd_name);
+
+       /* 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);
+
+       retval = ERROR_OK;
+
+free_full_name:
+       free((void *)full_name);
+       return retval;
+}
+
 struct command* register_command(struct command_context *context,
                struct command *parent, const struct command_registration *cr)
 {
@@ -282,26 +328,22 @@ struct command* register_command(struct command_context *context,
                return c;
        }
 
-       c = command_new(context, parent, name, cr->handler, cr->mode, cr->help, cr->usage);
-       /* if allocation failed or it is a placeholder (no handler), we're done */
-       if (NULL == c || NULL == c->handler)
-               return c;
-
-       const char *full_name = command_name(c, '_');
-
-       const char *ocd_name = alloc_printf("ocd_%s", full_name);
-       Jim_CreateCommand(interp, ocd_name, script_command, c, NULL);
-       free((void *)ocd_name);
+       c = command_new(context, parent, cr);
+       if (NULL == c)
+               return NULL;
 
-       /* 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);
-       Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
-       free((void *)override_name);
+       if (NULL != c->handler)
+       {
+               int retval = register_command_handler(c);
+               if (ERROR_OK != retval)
+               {
+                       unregister_command(context, parent, name);
+                       return NULL;
+               }
+       }
 
-       free((void *)full_name);
+       if (NULL != cr->jim_handler && NULL == parent)
+               Jim_CreateCommand(interp, cr->name, cr->jim_handler, cr->jim_handler_data, NULL);
 
        return c;
 }
@@ -309,18 +351,36 @@ struct command* register_command(struct command_context *context,
 int register_commands(struct command_context *cmd_ctx, struct command *parent,
                const struct command_registration *cmds)
 {
+       int retval = ERROR_OK;
        unsigned i;
-       for (i = 0; cmds[i].name; i++)
+       for (i = 0; cmds[i].name || cmds[i].chain; i++)
        {
-               struct command *c = register_command(cmd_ctx, parent, cmds + i);
-               if (NULL != c)
-                       continue;
+               const struct command_registration *cr = cmds + i;
 
+               struct command *c = NULL;
+               if (NULL != cr->name)
+               {
+                       c = register_command(cmd_ctx, parent, cr);
+                       if (NULL == c)
+                       {
+                               retval = ERROR_FAIL;
+                               break;
+                       }
+               }
+               if (NULL != cr->chain)
+               {
+                       struct command *p = c ? : parent;
+                       retval = register_commands(cmd_ctx, p, cr->chain);
+                       if (ERROR_OK != retval)
+                               break;
+               }
+       }
+       if (ERROR_OK != retval)
+       {
                for (unsigned j = 0; j < i; j++)
                        unregister_command(cmd_ctx, parent, cmds[j].name);
-               return ERROR_FAIL;
        }
-       return ERROR_OK;
+       return retval;
 }
 
 int unregister_all_commands(struct command_context *context,
@@ -799,6 +859,76 @@ COMMAND_HANDLER(handle_usage_command)
        return CALL_COMMAND_HANDLER(command_help_show, c, 0, false);
 }
 
+static int command_unknown_find(unsigned argc, Jim_Obj *const *argv,
+               struct command *head, struct command **out)
+{
+       if (0 == argc)
+               return argc;
+       struct command *c = command_find(head, Jim_GetString(argv[0], NULL));
+       if (NULL == c)
+               return argc;
+       *out = c;
+       return command_unknown_find(--argc, ++argv, (*out)->children, out);
+}
+
+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);
+
+       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);
+       // if nothing could be consumed, then it's really an unknown command
+       if (remaining == argc - 1)
+       {
+               const char *cmd = Jim_GetString(argv[1], NULL);
+               LOG_ERROR("Unknown command:\n  %s", cmd);
+               return JIM_OK;
+       }
+
+       bool found = true;
+       Jim_Obj *const *start;
+       unsigned count;
+       if (c->handler || c->jim_handler)
+       {
+               // include the command name in the list
+               count = remaining + 1;
+               start = argv + (argc - remaining - 1);
+       }
+       else
+       {
+               c = command_find(cmd_ctx->commands, "help");
+               if (NULL == c)
+               {
+                       LOG_ERROR("unknown command, but help is missing too");
+                       return JIM_ERR;
+               }
+               count = argc - remaining;
+               start = argv;
+               found = false;
+       }
+       // pass the command through to the intended handler
+       if (c->jim_handler)
+       {
+               interp->cmdPrivData = c->jim_handler_data;
+               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;
+
+       int retval = run_command(cmd_ctx, c, words, nwords);
+
+       script_command_args_free(words, nwords);
+
+       if (!found && ERROR_OK == retval)
+               retval = ERROR_FAIL;
+
+       return retval;
+}
 
 int help_add_command(struct command_context *cmd_ctx, struct command *parent,
                const char *cmd_name, const char *help_text, const char *usage)
@@ -900,6 +1030,39 @@ COMMAND_HANDLER(handle_sleep_command)
        return ERROR_OK;
 }
 
+static const struct command_registration command_builtin_handlers[] = {
+       {
+               .name = "add_help_text",
+               .handler = &handle_help_add_command,
+               .mode = COMMAND_ANY,
+               .help = "add new command help text",
+               .usage = "<command> [...] <help_text>]",
+       },
+       {
+               .name = "sleep",
+               .handler = &handle_sleep_command,
+               .mode = COMMAND_ANY,
+               .help = "sleep for n milliseconds.  "
+                       "\"busy\" will busy wait",
+               .usage = "<n> [busy]",
+       },
+       {
+               .name = "help",
+               .handler = &handle_help_command,
+               .mode = COMMAND_ANY,
+               .help = "show built-in command help",
+               .usage = "[<command_name> ...]",
+       },
+       {
+               .name = "usage",
+               .handler = &handle_usage_command,
+               .mode = COMMAND_ANY,
+               .help = "show command usage",
+               .usage = "[<command_name> ...]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
 struct command_context* command_init(const char *startup_tcl)
 {
        struct command_context* context = malloc(sizeof(struct command_context));
@@ -945,6 +1108,7 @@ 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);
@@ -959,10 +1123,7 @@ struct command_context* command_init(const char *startup_tcl)
        interp->cb_fflush = openocd_jim_fflush;
        interp->cb_fgets = openocd_jim_fgets;
 
-       COMMAND_REGISTER(context, NULL, "add_help_text",
-                       handle_help_add_command, COMMAND_ANY,
-                       "<command> [...] <help_text>] - "
-                       "add new command help text");
+       register_commands(context, NULL, command_builtin_handlers);
 
 #if !BUILD_ECOSBOARD
        Jim_EventLoopOnLoad(interp);
@@ -976,19 +1137,6 @@ struct command_context* command_init(const char *startup_tcl)
        }
        Jim_DeleteAssocData(interp, "context");
 
-       COMMAND_REGISTER(context, NULL, "sleep",
-                       handle_sleep_command, COMMAND_ANY,
-                       "<n> [busy] - sleep for n milliseconds. "
-                       "\"busy\" means busy wait");
-
-       COMMAND_REGISTER(context, NULL, "help",
-                       &handle_help_command, COMMAND_ANY,
-                       "[<command_name> ...] - show built-in command help");
-       COMMAND_REGISTER(context, NULL, "usage",
-                       &handle_usage_command, COMMAND_ANY,
-                       "[<command_name> ...] | "
-                       "show command usage");
-
        return context;
 }
 
@@ -1015,18 +1163,6 @@ void process_jim_events(void)
 #endif
 }
 
-void register_jim(struct command_context *cmd_ctx, const char *name,
-               Jim_CmdProc cmd, const char *help)
-{
-       Jim_CreateCommand(interp, name, cmd, NULL, NULL);
-
-       Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
-       Jim_ListAppendElement(interp, cmd_list,
-                       Jim_NewStringObj(interp, name, -1));
-
-       help_add_command(cmd_ctx, NULL, name, help, NULL);
-}
-
 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
        int parse##name(const char *str, type *ul) \
        { \