#include "jim-eventloop.h"
-int fast_and_dangerous = 0;
Jim_Interp *interp = NULL;
static int run_command(struct command_context *context,
}
}
+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
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");
log_add_callback(tcl_output, tclOutput);
- // turn words[0] into CMD_ARGV[-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);
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)
* 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;
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);
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);
+ 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 : "<global>");
+ 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, '_');
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;
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 */
struct command_invocation cmd = {
.ctx = context,
- .argc = num_words - start_word - 1,
- .argv = words + start_word + 1,
+ .name = c->name,
+ .argc = num_words - 1,
+ .argv = words + 1,
};
int retval = c->handler(&cmd);
if (retval == ERROR_COMMAND_SYNTAX_ERROR)
return ERROR_OK;
}
-COMMAND_HANDLER(handle_fast_command)
-{
- if (CMD_ARGC != 1)
- return ERROR_COMMAND_SYNTAX_ERROR;
-
- fast_and_dangerous = strcmp("enable", CMD_ARGV[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;
handle_sleep_command, COMMAND_ANY,
"<n> [busy] - sleep for n milliseconds. "
"\"busy\" means busy wait");
- register_command(context, NULL, "fast",
- handle_fast_command, COMMAND_ANY,
- "fast <enable/disable> - place at beginning of "
- "config files. Sets defaults to fast and dangerous.");
return context;
}
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) \
{ \
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;
+}
+
+