+ struct command *c = CMD_CTX->commands;
+
+ 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);
+
+ 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);
+}
+
+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)
+{
+ struct command **head = command_list_for_parent(cmd_ctx, parent);
+ struct command *nc = command_find(*head, cmd_name);
+ if (NULL == nc)
+ {
+ // add a new command with help text
+ struct command_registration cr = {
+ .name = cmd_name,
+ .mode = COMMAND_ANY,
+ .help = help_text,
+ .usage = usage,
+ };
+ nc = register_command(cmd_ctx, parent, &cr);
+ if (NULL == nc)
+ {
+ LOG_ERROR("failed to add '%s' help text", cmd_name);
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("added '%s' help text", cmd_name);
+ }
+ else
+ {
+ bool replaced = false;
+ if (nc->help)
+ {
+ free((void *)nc->help);
+ replaced = true;
+ }
+ nc->help = strdup(help_text);
+
+ if (replaced)
+ LOG_INFO("replaced existing '%s' help", cmd_name);
+ else
+ LOG_DEBUG("added '%s' help text", cmd_name);
+ }
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_help_add_command)
+{
+ if (CMD_ARGC < 2)
+ {
+ LOG_ERROR("%s: insufficient arguments", CMD_NAME);
+ return ERROR_INVALID_ARGUMENTS;
+ }
+
+ // save help text and remove it from argument list
+ const char *help_text = CMD_ARGV[--CMD_ARGC];
+ // likewise for the leaf command name
+ const char *cmd_name = CMD_ARGV[--CMD_ARGC];
+
+ struct command *c = NULL;
+ if (CMD_ARGC > 0)
+ {
+ c = CMD_CTX->commands;
+ int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
+ if (ERROR_OK != retval)
+ return retval;
+ }
+ return help_add_command(CMD_CTX, c, cmd_name, help_text, NULL);
+}
+
+/* sleep command sleeps for <n> miliseconds
+ * this is useful in target startup scripts
+ */
+COMMAND_HANDLER(handle_sleep_command)
+{
+ bool busy = false;
+ if (CMD_ARGC == 2)
+ {
+ if (strcmp(CMD_ARGV[1], "busy") == 0)
+ busy = true;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ else if (CMD_ARGC < 1 || CMD_ARGC > 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ unsigned long duration = 0;
+ int retval = parse_ulong(CMD_ARGV[0], &duration);
+ if (ERROR_OK != retval)
+ return retval;
+
+ if (!busy)
+ {
+ long long then = timeval_ms();
+ while (timeval_ms() - then < (long long)duration)
+ {
+ target_call_timer_callbacks_now();
+ usleep(1000);
+ }
+ }
+ else
+ busy_sleep(duration);
+
+ 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));