X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fhelper%2Fcommand.c;h=f135bb03cb2f8dbed1e7d75a2d2ed042e3e424ef;hp=7edd585f060a3fecf1a4849495dc5568269eb71d;hb=5458fef43ca7072312440301a9469c686ca641e2;hpb=df9b12695f4ede8144491d257ea236cdbca0a15c diff --git a/src/helper/command.c b/src/helper/command.c index 7edd585f06..f135bb03cb 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -44,7 +44,6 @@ #include "jim-eventloop.h" -int fast_and_dangerous = 0; Jim_Interp *interp = NULL; static int run_command(struct command_context *context, @@ -76,15 +75,45 @@ void script_debug(Jim_Interp *interp, const char *name, } } +static void script_command_args_free(const char **words, unsigned nwords) +{ + for (unsigned i = 0; i < nwords; i++) + free((void *)words[i]); + free(words); +} +static const char **script_command_args_alloc( + unsigned argc, Jim_Obj *const *argv, unsigned *nwords) +{ + const char **words = malloc(argc * sizeof(char *)); + if (NULL == words) + return NULL; + + unsigned i; + for (i = 0; i < argc; i++) + { + int len; + const char *w = Jim_GetString(argv[i], &len); + /* a comment may end the line early */ + if (*w == '#') + break; + + words[i] = strdup(w); + if (words[i] == NULL) + { + script_command_args_free(words, i); + return NULL; + } + } + *nwords = i; + return words; +} + static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { /* the private data is stashed in the interp structure */ struct command *c; struct command_context *context; int retval; - int i; - int nwords; - char **words; /* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might * get overwritten by running other Jim commands! Treat it as an @@ -102,28 +131,10 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) script_debug(interp, c->name, argc, argv); - words = malloc(sizeof(char *) * (argc + 1)); - words[0] = c->name; - for (i = 0; i < argc; i++) - { - int len; - const char *w = Jim_GetString(argv[i], &len); - if (*w=='#') - { - /* hit an end of line comment */ - break; - } - words[i + 1] = strdup(w); - if (words[i + 1] == NULL) - { - int j; - for (j = 0; j < i; j++) - free(words[j + 1]); - free(words); - return JIM_ERR; - } - } - nwords = i; + unsigned nwords; + const char **words = script_command_args_alloc(argc, argv, &nwords); + if (NULL == words) + return JIM_ERR; /* grab the command context from the associated data */ context = Jim_GetAssocData(interp, "context"); @@ -142,8 +153,7 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) log_add_callback(tcl_output, tclOutput); - // turn words[0] into args[-1] with this cast - retval = run_command(context, c, (const char **)words + 1, nwords); + retval = run_command(context, c, (const char **)words, nwords); log_remove_callback(tcl_output, tclOutput); @@ -151,9 +161,7 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv) Jim_SetResult(interp, tclOutput); Jim_DecrRefCount(interp, tclOutput); - for (i = 0; i < nwords; i++) - free(words[i + 1]); - free(words); + script_command_args_free(words, nwords); int *return_retval = Jim_GetAssocData(interp, "retval"); if (return_retval != NULL) @@ -197,10 +205,9 @@ static void command_helptext_add(Jim_Obj *cmd_list, const char *help) * Find a command by name from a list of commands. * @returns The named command if found, or NULL. */ -static struct command *command_find(struct command **head, const char *name) +static struct command *command_find(struct command *head, const char *name) { - assert(head); - for (struct command *cc = *head; cc; cc = cc->next) + for (struct command *cc = head; cc; cc = cc->next) { if (strcmp(cc->name, name) == 0) return cc; @@ -226,33 +233,73 @@ static void command_add_child(struct command **head, struct command *c) cc->next = c; } -struct command* register_command(struct command_context *context, - struct command *parent, char *name, command_handler_t handler, - enum command_mode mode, char *help) +static struct command **command_list_for_parent( + struct command_context *cmd_ctx, struct command *parent) { - if (!context || !name) - return NULL; + return parent ? &parent->children : &cmd_ctx->commands; +} - struct command **head = parent ? &parent->children : &context->commands; - struct command *c = command_find(head, name); - if (NULL != c) - return c; +static struct command *command_new(struct command_context *cmd_ctx, + struct command *parent, const char *name, + command_handler_t handler, enum command_mode mode, + const char *help) +{ + assert(name); - c = malloc(sizeof(struct command)); + struct command *c = malloc(sizeof(struct command)); + memset(c, 0, sizeof(struct command)); c->name = strdup(name); + if (help) + c->help = strdup(help); c->parent = parent; - c->children = NULL; c->handler = handler; c->mode = mode; - c->next = NULL; - command_add_child(head, c); + command_add_child(command_list_for_parent(cmd_ctx, parent), c); command_helptext_add(command_name_list(c), help); - /* just a placeholder, no handler */ - if (c->handler == NULL) + return c; +} +static void command_free(struct command *c) +{ + /// @todo if command has a handler, unregister its jim command! + + while (NULL != c->children) + { + struct command *tmp = c->children; + c->children = tmp->next; + command_free(tmp); + } + + if (c->name) + free(c->name); + if (c->help) + free((void*)c->help); + free(c); +} + +struct command* register_command(struct command_context *context, + struct command *parent, const char *name, + command_handler_t handler, enum command_mode mode, + const char *help) +{ + if (!context || !name) + return NULL; + + struct command **head = command_list_for_parent(context, parent); + struct command *c = command_find(*head, name); + if (NULL != c) + { + LOG_ERROR("command '%s' is already registered in '%s' context", + name, parent ? parent->name : ""); + return c; + } + + c = command_new(context, parent, name, handler, mode, help); + /* if allocation failed or it is a placeholder (no handler), we're done */ + if (NULL == c || NULL == c->handler) return c; const char *full_name = command_name(c, '_'); @@ -274,85 +321,43 @@ struct command* register_command(struct command_context *context, return c; } -int unregister_all_commands(struct command_context *context) +int unregister_all_commands(struct command_context *context, + struct command *parent) { - struct command *c, *c2; - if (context == NULL) return ERROR_OK; - while (NULL != context->commands) + struct command **head = command_list_for_parent(context, parent); + while (NULL != *head) { - c = context->commands; - - while (NULL != c->children) - { - c2 = c->children; - c->children = c->children->next; - free(c2->name); - c2->name = NULL; - free(c2); - c2 = NULL; - } - - context->commands = context->commands->next; - - free(c->name); - c->name = NULL; - free(c); - c = NULL; + struct command *tmp = *head; + *head = tmp->next; + command_free(tmp); } return ERROR_OK; } -int unregister_command(struct command_context *context, char *name) +int unregister_command(struct command_context *context, + struct command *parent, const char *name) { - struct command *c, *p = NULL, *c2; - if ((!context) || (!name)) return ERROR_INVALID_ARGUMENTS; - /* find command */ - c = context->commands; - - while (NULL != c) + struct command *p = NULL; + struct command **head = command_list_for_parent(context, parent); + for (struct command *c = *head; NULL != c; p = c, c = c->next) { - if (strcmp(name, c->name) == 0) - { - /* unlink command */ - if (p) - { - p->next = c->next; - } - else - { - /* first element in command list */ - context->commands = c->next; - } + if (strcmp(name, c->name) != 0) + continue; - /* unregister children */ - while (NULL != c->children) - { - c2 = c->children; - c->children = c->children->next; - free(c2->name); - c2->name = NULL; - free(c2); - c2 = NULL; - } - - /* delete command */ - free(c->name); - c->name = NULL; - free(c); - c = NULL; - return ERROR_OK; - } + if (p) + p->next = c->next; + else + *head = c->next; - /* remember the last command for unlinking */ - p = c; - c = c->next; + command_free(c); + return ERROR_OK; } return ERROR_OK; @@ -439,7 +444,6 @@ char *command_name(struct command *c, char delim) static int run_command(struct command_context *context, struct command *c, const char *words[], unsigned num_words) { - int start_word = 0; if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode))) { /* Config commands can not run after the config stage */ @@ -447,9 +451,13 @@ static int run_command(struct command_context *context, return ERROR_FAIL; } - unsigned argc = num_words - start_word - 1; - const char **args = words + start_word + 1; - int retval = c->handler(context, args, argc); + struct command_invocation cmd = { + .ctx = context, + .name = c->name, + .argc = num_words - 1, + .argv = words + 1, + }; + int retval = c->handler(&cmd); if (retval == ERROR_COMMAND_SYNTAX_ERROR) { /* Print help for command */ @@ -717,24 +725,70 @@ static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 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); + +static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n) +{ + for (struct command *c = head; NULL != c; c = c->next) + CALL_COMMAND_HANDLER(command_help_show, c, n); + return ERROR_OK; +} +static COMMAND_HELPER(command_help_show, struct command *c, unsigned n) +{ + command_run_linef(CMD_CTX, "cmd_help {%s} {%s} %d", command_name(c, ' '), + c->help ? : "no help available", n); + + if (++n >= 2) + return ERROR_OK; + + return CALL_COMMAND_HANDLER(command_help_show_list, c->children, n); +} +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); + + 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); +} + /* sleep command sleeps for miliseconds * this is useful in target startup scripts */ COMMAND_HANDLER(handle_sleep_command) { bool busy = false; - if (argc == 2) + if (CMD_ARGC == 2) { - if (strcmp(args[1], "busy") == 0) + if (strcmp(CMD_ARGV[1], "busy") == 0) busy = true; else return ERROR_COMMAND_SYNTAX_ERROR; } - else if (argc < 1 || argc > 2) + else if (CMD_ARGC < 1 || CMD_ARGC > 2) return ERROR_COMMAND_SYNTAX_ERROR; unsigned long duration = 0; - int retval = parse_ulong(args[0], &duration); + int retval = parse_ulong(CMD_ARGV[0], &duration); if (ERROR_OK != retval) return retval; @@ -753,21 +807,9 @@ COMMAND_HANDLER(handle_sleep_command) return ERROR_OK; } -COMMAND_HANDLER(handle_fast_command) -{ - if (argc != 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - fast_and_dangerous = strcmp("enable", args[0]) == 0; - - return ERROR_OK; -} - - -struct command_context* command_init() +struct command_context* command_init(const char *startup_tcl) { struct command_context* context = malloc(sizeof(struct command_context)); - extern const char startup_tcl[]; const char *HostOs; context->mode = COMMAND_EXEC; @@ -838,10 +880,10 @@ struct command_context* command_init() handle_sleep_command, COMMAND_ANY, " [busy] - sleep for n milliseconds. " "\"busy\" means busy wait"); - register_command(context, NULL, "fast", - handle_fast_command, COMMAND_ANY, - "fast - place at beginning of " - "config files. Sets defaults to fast and dangerous."); + + register_command(context, NULL, "help", + &handle_help_command, COMMAND_ANY, + "[ ...] - show built-in command help"); return context; } @@ -881,18 +923,6 @@ void register_jim(struct command_context *cmd_ctx, const char *name, command_helptext_add(cmd_list, help); } -/* return global variable long value or 0 upon failure */ -long jim_global_long(const char *variable) -{ - Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, variable, JIM_ERRMSG); - long t; - if (Jim_GetLong(interp, objPtr, &t) == JIM_OK) - { - return t; - } - return 0; -} - #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \ int parse##name(const char *str, type *ul) \ { \ @@ -953,3 +983,51 @@ DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX) DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX) DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX) DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX) + +static int command_parse_bool(const char *in, bool *out, + const char *on, const char *off) +{ + if (strcasecmp(in, on) == 0) + *out = true; + else if (strcasecmp(in, off) == 0) + *out = false; + else + return ERROR_COMMAND_SYNTAX_ERROR; + return ERROR_OK; +} + +int command_parse_bool_arg(const char *in, bool *out) +{ + if (command_parse_bool(in, out, "on", "off") == ERROR_OK) + return ERROR_OK; + if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK) + return ERROR_OK; + if (command_parse_bool(in, out, "true", "false") == ERROR_OK) + return ERROR_OK; + if (command_parse_bool(in, out, "yes", "no") == ERROR_OK) + return ERROR_OK; + if (command_parse_bool(in, out, "1", "0") == ERROR_OK) + return ERROR_OK; + return ERROR_INVALID_ARGUMENTS; +} + +COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label) +{ + switch (CMD_ARGC) { + case 1: { + const char *in = CMD_ARGV[0]; + if (command_parse_bool_arg(in, out) != ERROR_OK) + { + LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in); + return ERROR_INVALID_ARGUMENTS; + } + // fall through + } + case 0: + LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled"); + break; + default: + return ERROR_INVALID_ARGUMENTS; + } + return ERROR_OK; +}