#include "jim-eventloop.h"
+/* nice short description of source file */
+#define __THIS__FILE__ "command.c"
+
Jim_Interp *interp = NULL;
static int run_command(struct command_context *context,
Jim_AppendString(interp, tclOutput, string, strlen(string));
}
+static Jim_Obj *command_log_capture_start(Jim_Interp *interp)
+{
+ /* capture log output and return it. A garbage collect can
+ * happen, so we need a reference count to this object */
+ Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
+ if (NULL == tclOutput)
+ return NULL;
+ Jim_IncrRefCount(tclOutput);
+ log_add_callback(tcl_output, tclOutput);
+ return tclOutput;
+}
+
+static void command_log_capture_finish(Jim_Interp *interp, Jim_Obj *tclOutput)
+{
+ log_remove_callback(tcl_output, tclOutput);
+ Jim_SetResult(interp, tclOutput);
+ Jim_DecrRefCount(interp, tclOutput);
+}
+
+static int command_retval_set(Jim_Interp *interp, int retval)
+{
+ int *return_retval = Jim_GetAssocData(interp, "retval");
+ if (return_retval != NULL)
+ *return_retval = retval;
+
+ return (retval == ERROR_OK) ? JIM_OK : JIM_ERR;
+}
+
extern struct command_context *global_cmd_ctx;
void script_debug(Jim_Interp *interp, const char *name,
return cmd_ctx;
}
-static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+static int script_command_run(Jim_Interp *interp,
+ int argc, Jim_Obj *const *argv, struct command *c, bool capture)
{
- /* the private data is stashed in the interp structure */
- struct command *c;
- int retval;
-
- /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
- * get overwritten by running other Jim commands! Treat it as an
- * emphemeral global variable that is used in lieu of an argument
- * to the fn and fish it out manually.
- */
- c = interp->cmdPrivData;
- if (c == NULL)
- {
- LOG_ERROR("BUG: interp->cmdPrivData == NULL");
- return JIM_ERR;
- }
target_call_timer_callbacks_now();
LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
- script_debug(interp, c->name, argc, argv);
-
unsigned nwords;
const char **words = script_command_args_alloc(argc, argv, &nwords);
if (NULL == words)
return JIM_ERR;
- /* 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);
+ Jim_Obj *tclOutput = NULL;
+ if (capture)
+ tclOutput = command_log_capture_start(interp);
struct command_context *cmd_ctx = current_command_context();
- retval = run_command(cmd_ctx, c, (const char **)words, nwords);
-
- log_remove_callback(tcl_output, tclOutput);
+ int retval = run_command(cmd_ctx, c, (const char **)words, nwords);
- /* We dump output into this local variable */
- Jim_SetResult(interp, tclOutput);
- Jim_DecrRefCount(interp, tclOutput);
+ if (capture)
+ command_log_capture_finish(interp, tclOutput);
script_command_args_free(words, nwords);
+ return command_retval_set(interp, retval);
+}
- int *return_retval = Jim_GetAssocData(interp, "retval");
- if (return_retval != NULL)
- {
- *return_retval = retval;
- }
+static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ /* the private data is stashed in the interp structure */
- return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
+ struct command *c = interp->cmdPrivData;
+ assert(c);
+ script_debug(interp, c->name, argc, argv);
+ return script_command_run(interp, argc, argv, c, true);
}
-/* nice short description of source file */
-#define __THIS__FILE__ "command.c"
+static struct command *command_root(struct command *c)
+{
+ while (NULL != c->parent)
+ c = c->parent;
+ return c;
+}
/**
* Find a command by name from a list of commands.
c->usage = strdup(cr->usage);
c->parent = parent;
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);
if (NULL == full_name)
return retval;
- const char *ocd_name = alloc_printf("ocd_%s", full_name);
- if (NULL == full_name)
- goto free_full_name;
+ if (NULL != c->handler)
+ {
+ 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);
+ 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)
+ if (NULL == override_name)
goto free_full_name;
Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
}
c = command_new(context, parent, cr);
- /* if allocation failed or it is a placeholder (no handler), we're done */
- if (NULL == c || NULL == c->handler)
- return c;
+ if (NULL == c)
+ return NULL;
- int retval = register_command_handler(c);
- if (ERROR_OK != retval)
+ if (NULL != c->handler)
{
- unregister_command(context, parent, name);
- c = NULL;
+ int retval = register_command_handler(command_root(c));
+ if (ERROR_OK != retval)
+ {
+ unregister_command(context, parent, name);
+ return NULL;
+ }
}
+
+ if (NULL != cr->jim_handler && NULL == parent)
+ Jim_CreateCommand(interp, cr->name, cr->jim_handler, cr->jim_handler_data, NULL);
+
return c;
}
{
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);
+ Jim_Obj *tclOutput = command_log_capture_start(interp);
- log_add_callback(tcl_output, tclOutput);
-
- retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
-
- log_remove_callback(tcl_output, tclOutput);
+ const char *str = Jim_GetString(argv[1], NULL);
+ int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
- /* We dump output into this local variable */
- Jim_SetResult(interp, tclOutput);
- Jim_DecrRefCount(interp, tclOutput);
+ command_log_capture_finish(interp, tclOutput);
return retcode;
}
if (0 == CMD_ARGC)
return ERROR_INVALID_ARGUMENTS;
*out = command_find(head, CMD_ARGV[0]);
+ if (NULL == *out && strncmp(CMD_ARGV[0], "ocd_", 4) == 0)
+ *out = command_find(head, CMD_ARGV[0] + 4);
if (NULL == *out)
return ERROR_INVALID_ARGUMENTS;
if (--CMD_ARGC == 0)
CALL_COMMAND_HANDLER(command_help_show, c, n, show_help);
return ERROR_OK;
}
+
+#define HELP_LINE_WIDTH(_n) (int)(76 - (2 * _n))
+
+static void command_help_show_indent(unsigned n)
+{
+ for (unsigned i = 0; i < n; i++)
+ LOG_USER_N(" ");
+}
+static void command_help_show_wrap(const char *str, unsigned n, unsigned n2)
+{
+ const char *cp = str, *last = str;
+ while (*cp)
+ {
+ const char *next = last;
+ do {
+ cp = next;
+ do {
+ next++;
+ } while (*next != ' ' && *next != '\t' && *next != '\0');
+ } while ((next - last < HELP_LINE_WIDTH(n)) && *next != '\0');
+ if (next - last < HELP_LINE_WIDTH(n))
+ cp = next;
+ command_help_show_indent(n);
+ LOG_USER_N("%.*s", (int)(cp - last), last);
+ LOG_USER_N("\n");
+ last = cp + 1;
+ n = n2;
+ }
+}
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_help_show_indent(n);
+ LOG_USER_N("%s", command_name(c, ' '));
+ if (c->usage) {
+ LOG_USER_N(" ");
+ command_help_show_wrap(c->usage, 0, n + 5);
}
- command_run_linef(CMD_CTX, "cmd_help {%s} {%s%s%s} %d",
- command_name(c, ' '), usage, sep, help, n);
-
+ else
+ LOG_USER_N("\n");
+ if (show_help && c->help)
+ command_help_show_wrap(c->help, n + 3, n + 3);
if (++n >= 2)
return ERROR_OK;
}
COMMAND_HANDLER(handle_help_command)
{
- struct command *c = CMD_CTX->commands;
+ bool full = strcmp(CMD_NAME, "help") == 0;
- 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);
+ return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, full);
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);
+ return CALL_COMMAND_HANDLER(command_help_show, c, 0, full);
}
static int command_unknown_find(unsigned argc, Jim_Obj *const *argv,
- struct command *head, struct command **out)
+ struct command *head, struct command **out, bool top_level)
{
if (0 == argc)
return argc;
- struct command *c = command_find(head, Jim_GetString(argv[0], NULL));
+ 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);
+ return command_unknown_find(--argc, ++argv, (*out)->children, out, false);
}
static int command_unknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
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);
+ int remaining = command_unknown_find(argc - 1, argv + 1, c, &c, true);
// if nothing could be consumed, then it's really an unknown command
if (remaining == argc - 1)
{
bool found = true;
Jim_Obj *const *start;
unsigned count;
- if (c->handler)
+ if (c->handler || c->jim_handler)
{
// include the command name in the list
count = remaining + 1;
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;
+ return script_command_run(interp, count, start, c, found);
+}
- int retval = run_command(cmd_ctx, c, words, nwords);
+static int jim_command_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ if (1 == argc)
+ return JIM_ERR;
- script_command_args_free(words, nwords);
+ 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, 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 (!found && ERROR_OK == retval)
- retval = ERROR_FAIL;
+ if (c->jim_handler)
+ Jim_SetResultString(interp, "native", -1);
+ else if (c->handler)
+ Jim_SetResultString(interp, "simple", -1);
+ else
+ Jim_SetResultString(interp, "group", -1);
- return retval;
+ return JIM_OK;
}
int help_add_command(struct command_context *cmd_ctx, struct command *parent,
return ERROR_FAIL;
}
LOG_DEBUG("added '%s' help text", cmd_name);
+ return ERROR_OK;
}
- else
+ if (help_text)
{
bool replaced = false;
if (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((void *)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;
}
}
// save help text and remove it from argument list
- const char *help_text = CMD_ARGV[--CMD_ARGC];
+ const char *str = CMD_ARGV[--CMD_ARGC];
+ const char *help = !strcmp(CMD_NAME, "add_help_text") ? str : NULL;
+ const char *usage = !strcmp(CMD_NAME, "add_usage_text") ? str : NULL;
+ if (!help && !usage)
+ {
+ LOG_ERROR("command name '%s' is unknown", CMD_NAME);
+ return ERROR_INVALID_ARGUMENTS;
+ }
// likewise for the leaf command name
const char *cmd_name = CMD_ARGV[--CMD_ARGC];
if (ERROR_OK != retval)
return retval;
}
- return help_add_command(CMD_CTX, c, cmd_name, help_text, NULL);
+ return help_add_command(CMD_CTX, c, cmd_name, help, usage);
}
/* sleep command sleeps for <n> miliseconds
return ERROR_OK;
}
+static const struct command_registration command_subcommand_handlers[] = {
+ {
+ .name = "type",
+ .mode = COMMAND_ANY,
+ .jim_handler = &jim_command_type,
+ .usage = "<name> ...",
+ .help = "Returns the type of built-in command:"
+ "'native', 'simple', 'group', or 'unknown'",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
static const struct command_registration command_builtin_handlers[] = {
{
.name = "add_help_text",
.help = "add new command help text",
.usage = "<command> [...] <help_text>]",
},
+ {
+ .name = "add_usage_text",
+ .handler = &handle_help_add_command,
+ .mode = COMMAND_ANY,
+ .help = "add new command usage text",
+ .usage = "<command> [...] <usage_text>]",
+ },
{
.name = "sleep",
.handler = &handle_sleep_command,
.name = "help",
.handler = &handle_help_command,
.mode = COMMAND_ANY,
- .help = "show built-in command help",
- .usage = "[<command_name> ...]",
+ .help = "show full command help",
+ .usage = "[<command> ...]",
},
{
.name = "usage",
- .handler = &handle_usage_command,
+ .handler = &handle_help_command,
.mode = COMMAND_ANY,
- .help = "show command usage",
- .usage = "[<command_name> ...]",
+ .help = "show basic command usage",
+ .usage = "[<command> ...]",
+ },
+ {
+ .name = "command",
+ .mode= COMMAND_ANY,
+ .help = "core command group (introspection)",
+ .chain = command_subcommand_handlers,
},
COMMAND_REGISTRATION_DONE
};
#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) \
{ \