+ 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;
+ }
+ count = argc - remaining;
+ start = argv;
+ found = false;
+ }
+ /* pass the command through to the intended handler */
+ if (c->jim_handler) {
+ if (!command_can_run(cmd_ctx, c))
+ return JIM_ERR;
+
+ interp->cmdPrivData = c->jim_handler_data;
+ return (*c->jim_handler)(interp, count, start);
+ }
+
+ return script_command_run(interp, count, start, c, found);
+}
+
+static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ struct command_context *cmd_ctx = current_command_context(interp);
+ enum command_mode mode;
+
+ if (argc > 1) {
+ 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 */
+ if (remaining == argc - 1) {
+ Jim_SetResultString(interp, "unknown", -1);
+ return JIM_OK;
+ }
+ mode = c->mode;
+ } else
+ mode = cmd_ctx->mode;
+
+ const char *mode_str;
+ switch (mode) {
+ case COMMAND_ANY:
+ mode_str = "any";
+ break;
+ case COMMAND_CONFIG:
+ mode_str = "config";
+ break;
+ case COMMAND_EXEC:
+ mode_str = "exec";
+ break;
+ default:
+ mode_str = "unknown";
+ break;
+ }
+ Jim_SetResultString(interp, mode_str, -1);
+ return JIM_OK;
+}
+
+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(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 */
+ if (remaining == argc - 1) {
+ Jim_SetResultString(interp, "unknown", -1);
+ return JIM_OK;
+ }
+
+ if (c->jim_handler)
+ Jim_SetResultString(interp, "native", -1);
+ else if (c->handler)
+ Jim_SetResultString(interp, "simple", -1);
+ else if (remaining == 0)
+ Jim_SetResultString(interp, "group", -1);
+ else
+ Jim_SetResultString(interp, "unknown", -1);
+
+ return JIM_OK;
+}
+
+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);
+ return ERROR_OK;
+ }
+ if (help_text) {
+ bool replaced = false;
+ if (nc->help) {
+ free(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);
+ }
+ if (usage) {
+ bool replaced = false;
+ if (nc->usage) {
+ free(nc->usage);
+ replaced = true;
+ }
+ nc->usage = strdup(usage);
+ if (replaced)
+ LOG_INFO("replaced existing '%s' usage", cmd_name);
+ else
+ LOG_DEBUG("added '%s' usage 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_COMMAND_SYNTAX_ERROR;
+ }