+static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
+ bool show_help, const char *cmd_match)
+{
+ char *cmd_name = command_name(c, ' ');
+ if (NULL == cmd_name)
+ return ERROR_FAIL;
+
+ /* If the match string occurs anywhere, we print out
+ * stuff for this command. */
+ bool is_match = (strstr(cmd_name, cmd_match) != NULL) ||
+ ((c->usage != NULL) && (strstr(c->usage, cmd_match) != NULL)) ||
+ ((c->help != NULL) && (strstr(c->help, cmd_match) != NULL));
+
+ if (is_match) {
+ command_help_show_indent(n);
+ LOG_USER_N("%s", cmd_name);
+ }
+ free(cmd_name);
+
+ if (is_match) {
+ if (c->usage && strlen(c->usage) > 0) {
+ LOG_USER_N(" ");
+ command_help_show_wrap(c->usage, 0, n + 5);
+ } else
+ LOG_USER_N("\n");
+ }
+
+ if (is_match && show_help) {
+ char *msg;
+
+ /* Normal commands are runtime-only; highlight exceptions */
+ if (c->mode != COMMAND_EXEC) {
+ const char *stage_msg = "";
+
+ switch (c->mode) {
+ case COMMAND_CONFIG:
+ stage_msg = " (configuration command)";
+ break;
+ case COMMAND_ANY:
+ stage_msg = " (command valid any time)";
+ break;
+ default:
+ stage_msg = " (?mode error?)";
+ break;
+ }
+ msg = alloc_printf("%s%s", c->help ? : "", stage_msg);
+ } else
+ msg = alloc_printf("%s", c->help ? : "");
+
+ if (NULL != msg) {
+ command_help_show_wrap(msg, n + 3, n + 3);
+ free(msg);
+ } else
+ return -ENOMEM;
+ }
+
+ if (++n > 5) {
+ LOG_ERROR("command recursion exceeded");
+ return ERROR_FAIL;
+ }
+
+ return CALL_COMMAND_HANDLER(command_help_show_list,
+ c->children, n, show_help, cmd_match);
+}
+
+COMMAND_HANDLER(handle_help_command)
+{
+ bool full = strcmp(CMD_NAME, "help") == 0;
+ int retval;
+ struct command *c = CMD_CTX->commands;
+ char *cmd_match = NULL;
+
+ if (CMD_ARGC == 0)
+ cmd_match = "";
+ else if (CMD_ARGC >= 1) {
+ unsigned i;
+
+ for (i = 0; i < CMD_ARGC; ++i) {
+ if (NULL != cmd_match) {
+ char *prev = cmd_match;
+
+ cmd_match = alloc_printf("%s %s", cmd_match, CMD_ARGV[i]);
+ free(prev);
+ if (NULL == cmd_match) {
+ LOG_ERROR("unable to build search string");
+ return -ENOMEM;
+ }
+ } else {
+ cmd_match = alloc_printf("%s", CMD_ARGV[i]);
+ if (NULL == cmd_match) {
+ LOG_ERROR("unable to build search string");
+ return -ENOMEM;
+ }
+ }
+ }
+ } else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ retval = CALL_COMMAND_HANDLER(command_help_show_list,
+ c, 0, full, cmd_match);
+
+ if (CMD_ARGC >= 1)
+ free(cmd_match);
+ return retval;
+}
+
+static int command_unknown_find(unsigned argc, Jim_Obj *const *argv,
+ struct command *head, struct command **out, bool top_level)
+{
+ if (0 == argc)
+ return argc;
+ 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, false);
+}
+
+static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ const char *cmd_name = Jim_GetString(argv[0], NULL);
+ 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(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 */
+ if (remaining == argc) {
+ const char *cmd = Jim_GetString(argv[0], 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, "usage");
+ if (NULL == c) {
+ LOG_ERROR("unknown command, but usage is missing too");
+ return JIM_ERR;