+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);
+
+ 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);
+
+ return retcode;
+}
+
+static COMMAND_HELPER(command_help_find, struct command *head,
+ struct command **out)
+{
+ if (0 == CMD_ARGC)
+ return ERROR_INVALID_ARGUMENTS;
+ *out = command_find(head, CMD_ARGV[0]);
+ if (NULL == *out)
+ return ERROR_INVALID_ARGUMENTS;
+ if (--CMD_ARGC == 0)
+ return ERROR_OK;
+ CMD_ARGV++;
+ return CALL_COMMAND_HANDLER(command_help_find, (*out)->children, out);
+}
+
+static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
+ bool show_help);
+
+static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n,
+ bool show_help)
+{
+ for (struct command *c = head; NULL != c; c = c->next)
+ CALL_COMMAND_HANDLER(command_help_show, c, n, show_help);
+ return ERROR_OK;
+}
+static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
+ bool show_help)
+{
+ const char *usage = c->usage ? : "";
+ const char *help = "";
+ const char *sep = "";
+ if (show_help && c->help)
+ {
+ help = c->help ? : "";
+ sep = c->usage ? " | " : "";
+ }
+ command_run_linef(CMD_CTX, "cmd_help {%s} {%s%s%s} %d",
+ command_name(c, ' '), usage, sep, help, n);
+
+ if (++n >= 2)
+ return ERROR_OK;
+
+ return CALL_COMMAND_HANDLER(command_help_show_list,
+ c->children, n, show_help);
+}
+COMMAND_HANDLER(handle_help_command)
+{
+ 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);
+}
+
+
+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)