rename CEIL as DIV_ROUND_UP
[fw/openocd] / src / helper / command.c
index bd317e6bdaa0995205618404100701a9ee195011..41af035675b55e12e1e0419f3ee2a9c66909f2e0 100644 (file)
 int fast_and_dangerous = 0;
 Jim_Interp *interp = NULL;
 
-int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
-int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
+static int run_command(struct command_context *context,
+               struct command *c, const char *words[], unsigned num_words);
 
-int run_command(command_context_t *context, command_t *c, char *words[], int num_words);
-
-static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
+static void tcl_output(void *privData, const char *file, unsigned line,
+               const char *function, const char *string)
 {
        Jim_Obj *tclOutput = (Jim_Obj *)privData;
-
        Jim_AppendString(interp, tclOutput, string, strlen(string));
 }
 
-extern command_context_t *global_cmd_ctx;
+extern struct command_context *global_cmd_ctx;
 
-void script_debug(Jim_Interp *interp, const char *name, int argc, Jim_Obj *const *argv)
+void script_debug(Jim_Interp *interp, const char *name,
+               unsigned argc, Jim_Obj *const *argv)
 {
-       int i;
-
        LOG_DEBUG("command - %s", name);
-       for (i = 0; i < argc; i++) {
+       for (unsigned i = 0; i < argc; i++)
+       {
                int len;
                const char *w = Jim_GetString(argv[i], &len);
 
@@ -81,8 +79,8 @@ void script_debug(Jim_Interp *interp, const char *name, int argc, Jim_Obj *const
 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
        /* the private data is stashed in the interp structure */
-       command_t *c;
-       command_context_t *context;
+       struct command *c;
+       struct command_context *context;
        int retval;
        int i;
        int nwords;
@@ -104,7 +102,8 @@ 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);
+       words = malloc(sizeof(char *) * (argc + 1));
+       words[0] = c->name;
        for (i = 0; i < argc; i++)
        {
                int len;
@@ -114,12 +113,12 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
                        /* hit an end of line comment */
                        break;
                }
-               words[i] = strdup(w);
-               if (words[i] == NULL)
+               words[i + 1] = strdup(w);
+               if (words[i + 1] == NULL)
                {
                        int j;
                        for (j = 0; j < i; j++)
-                               free(words[j]);
+                               free(words[j + 1]);
                        free(words);
                        return JIM_ERR;
                }
@@ -143,7 +142,8 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
        log_add_callback(tcl_output, tclOutput);
 
-       retval = run_command(context, c, words, nwords);
+       // turn words[0] into args[-1] with this cast
+       retval = run_command(context, c, (const char **)words + 1, nwords);
 
        log_remove_callback(tcl_output, tclOutput);
 
@@ -152,7 +152,7 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        Jim_DecrRefCount(interp, tclOutput);
 
        for (i = 0; i < nwords; i++)
-               free(words[i]);
+               free(words[i + 1]);
        free(words);
 
        int *return_retval = Jim_GetAssocData(interp, "retval");
@@ -164,111 +164,119 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
 }
 
+static Jim_Obj *command_name_list(struct command *c)
+{
+       Jim_Obj *cmd_list = c->parent ?
+                       command_name_list(c->parent) :
+                       Jim_NewListObj(interp, NULL, 0);
+       Jim_ListAppendElement(interp, cmd_list,
+                       Jim_NewStringObj(interp, c->name, -1));
+
+       return cmd_list;
+}
+
+static void command_helptext_add(Jim_Obj *cmd_list, const char *help)
+{
+       Jim_Obj *cmd_entry = Jim_NewListObj(interp, NULL, 0);
+       Jim_ListAppendElement(interp, cmd_entry, cmd_list);
+       Jim_ListAppendElement(interp, cmd_entry,
+                       Jim_NewStringObj(interp, help ? : "", -1));
+
+       /* accumulate help text in Tcl helptext list.  */
+       Jim_Obj *helptext = Jim_GetGlobalVariableStr(interp,
+                       "ocd_helptext", JIM_ERRMSG);
+       if (Jim_IsShared(helptext))
+               helptext = Jim_DuplicateObj(interp, helptext);
+       Jim_ListAppendElement(interp, helptext, cmd_entry);
+}
+
 /* nice short description of source file */
 #define __THIS__FILE__ "command.c"
 
-command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, 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)
+{
+       assert(head);
+       for (struct command *cc = *head; cc; cc = cc->next)
+       {
+               if (strcmp(cc->name, name) == 0)
+                       return cc;
+       }
+       return NULL;
+}
+
+/**
+ * Add the command to the end of linked list.
+ * @returns Returns false if the named command already exists in the list.
+ * Returns true otherwise.
+ */
+static void command_add_child(struct command **head, struct command *c)
 {
-       command_t *c, *p;
+       assert(head);
+       if (NULL == *head)
+       {
+               *head = c;
+               return;
+       }
+       struct command *cc = *head;
+       while (cc->next) cc = cc->next;
+       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)
+{
        if (!context || !name)
                return NULL;
 
-       c = malloc(sizeof(command_t));
+       struct command **head = parent ? &parent->children : &context->commands;
+       struct command *c = command_find(head, name);
+       if (NULL != c)
+               return c;
+
+       c = malloc(sizeof(struct command));
 
        c->name = strdup(name);
        c->parent = parent;
        c->children = NULL;
        c->handler = handler;
        c->mode = mode;
-       if (!help)
-               help="";
        c->next = NULL;
 
-       /* place command in tree */
-       if (parent)
-       {
-               if (parent->children)
-               {
-                       /* find last child */
-                       for (p = parent->children; p && p->next; p = p->next);
-                       if (p)
-                               p->next = c;
-               }
-               else
-               {
-                       parent->children = c;
-               }
-       }
-       else
-       {
-               if (context->commands)
-               {
-                       /* find last command */
-                       for (p = context->commands; p && p->next; p = p->next);
-                       if (p)
-                               p->next = c;
-               }
-               else
-               {
-                       context->commands = c;
-               }
-       }
+       command_add_child(head, c);
+
+       command_helptext_add(command_name_list(c), help);
 
        /* just a placeholder, no handler */
        if (c->handler == NULL)
                return c;
 
-       /* If this is a two level command, e.g. "flash banks", then the
-        * "unknown" proc in startup.tcl must redirect to  this command.
-        *
-        * "flash banks" is translated by "unknown" to "flash_banks"
-        * if such a proc exists
-        */
-       /* Print help for command */
-       const char *t1="";
-       const char *t2="";
-       const char *t3="";
-       /* maximum of two levels :-) */
-       if (c->parent != NULL)
-       {
-               t1 = c->parent->name;
-               t2="_";
-       }
-       t3 = c->name;
-       const char *full_name = alloc_printf("ocd_%s%s%s", t1, t2, t3);
-       Jim_CreateCommand(interp, full_name, script_command, c, NULL);
-       free((void *)full_name);
+       const char *full_name = command_name(c, '_');
+
+       const char *ocd_name = alloc_printf("ocd_%s", full_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%s%s {args} {if {[catch {eval ocd_%s%s%s $args}]==0} {return \"\"} else { return -code error }", t1, t2, t3, t1, t2, t3);
+       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);
        Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
        free((void *)override_name);
 
-       /* accumulate help text in Tcl helptext list.  */
-       Jim_Obj *helptext = Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
-       if (Jim_IsShared(helptext))
-               helptext = Jim_DuplicateObj(interp, helptext);
-       Jim_Obj *cmd_entry = Jim_NewListObj(interp, NULL, 0);
-
-       Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
-
-       /* maximum of two levels :-) */
-       if (c->parent != NULL)
-       {
-               Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
-       }
-       Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
+       free((void *)full_name);
 
-       Jim_ListAppendElement(interp, cmd_entry, cmd_list);
-       Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
-       Jim_ListAppendElement(interp, helptext, cmd_entry);
        return c;
 }
 
-int unregister_all_commands(command_context_t *context)
+int unregister_all_commands(struct command_context *context)
 {
-       command_t *c, *c2;
+       struct command *c, *c2;
 
        if (context == NULL)
                return ERROR_OK;
@@ -298,9 +306,9 @@ int unregister_all_commands(command_context_t *context)
        return ERROR_OK;
 }
 
-int unregister_command(command_context_t *context, char *name)
+int unregister_command(struct command_context *context, char *name)
 {
-       command_t *c, *p = NULL, *c2;
+       struct command *c, *p = NULL, *c2;
 
        if ((!context) || (!name))
                return ERROR_INVALID_ARGUMENTS;
@@ -350,14 +358,14 @@ int unregister_command(command_context_t *context, char *name)
        return ERROR_OK;
 }
 
-void command_output_text(command_context_t *context, const char *data)
+void command_output_text(struct command_context *context, const char *data)
 {
        if (context && context->output_handler && data) {
                context->output_handler(context, data);
        }
 }
 
-void command_print_sameline(command_context_t *context, const char *format, ...)
+void command_print_sameline(struct command_context *context, const char *format, ...)
 {
        char *string;
 
@@ -381,7 +389,7 @@ void command_print_sameline(command_context_t *context, const char *format, ...)
        va_end(ap);
 }
 
-void command_print(command_context_t *context, const char *format, ...)
+void command_print(struct command_context *context, const char *format, ...)
 {
        char *string;
 
@@ -406,7 +414,30 @@ void command_print(command_context_t *context, const char *format, ...)
        va_end(ap);
 }
 
-int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
+static char *__command_name(struct command *c, char delim, unsigned extra)
+{
+       char *name;
+       unsigned len = strlen(c->name);
+       if (NULL == c->parent) {
+               // allocate enough for the name, child names, and '\0'
+               name = malloc(len + extra + 1);
+               strcpy(name, c->name);
+       } else {
+               // parent's extra must include both the space and name
+               name = __command_name(c->parent, delim, 1 + len + extra);
+               char dstr[2] = { delim, 0 };
+               strcat(name, dstr);
+               strcat(name, c->name);
+       }
+       return name;
+}
+char *command_name(struct command *c, char delim)
+{
+       return __command_name(c, delim, 0);
+}
+
+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)))
@@ -416,21 +447,18 @@ int run_command(command_context_t *context, command_t *c, char *words[], int num
                return ERROR_FAIL;
        }
 
-       int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
+       unsigned argc = num_words - start_word - 1;
+       const char **args = words + start_word + 1;
+       int retval = c->handler(context, args, argc);
        if (retval == ERROR_COMMAND_SYNTAX_ERROR)
        {
                /* Print help for command */
-               const char *t1="";
-               const char *t2="";
-               const char *t3="";
-               /* maximum of two levels :-) */
-               if (c->parent != NULL)
-               {
-                       t1 = c->parent->name;
-                       t2=" ";
-               }
-               t3 = c->name;
-               command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
+               char *full_name = command_name(c, ' ');
+               if (NULL != full_name) {
+                       command_run_linef(context, "help %s", full_name);
+                       free(full_name);
+               } else
+                       retval = -ENOMEM;
        }
        else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
        {
@@ -447,7 +475,7 @@ int run_command(command_context_t *context, command_t *c, char *words[], int num
        return retval;
 }
 
-int command_run_line(command_context_t *context, char *line)
+int command_run_line(struct command_context *context, char *line)
 {
        /* all the parent commands have been registered with the interpreter
         * so, can just evaluate the line as a script and check for
@@ -517,7 +545,7 @@ int command_run_line(command_context_t *context, char *line)
        return retval;
 }
 
-int command_run_linef(command_context_t *context, const char *format, ...)
+int command_run_linef(struct command_context *context, const char *format, ...)
 {
        int retval = ERROR_FAIL;
        char *string;
@@ -532,22 +560,23 @@ int command_run_linef(command_context_t *context, const char *format, ...)
        return retval;
 }
 
-void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
+void command_set_output_handler(struct command_context* context,
+               command_output_handler_t output_handler, void *priv)
 {
        context->output_handler = output_handler;
        context->output_handler_priv = priv;
 }
 
-command_context_t* copy_command_context(command_context_t* context)
+struct command_context* copy_command_context(struct command_context* context)
 {
-       command_context_t* copy_context = malloc(sizeof(command_context_t));
+       struct command_context* copy_context = malloc(sizeof(struct command_context));
 
        *copy_context = *context;
 
        return copy_context;
 }
 
-int command_done(command_context_t *context)
+int command_done(struct command_context *context)
 {
        free(context);
        context = NULL;
@@ -688,9 +717,56 @@ static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        return retcode;
 }
 
-command_context_t* command_init()
+/* sleep command sleeps for <n> miliseconds
+ * this is useful in target startup scripts
+ */
+COMMAND_HANDLER(handle_sleep_command)
+{
+       bool busy = false;
+       if (argc == 2)
+       {
+               if (strcmp(args[1], "busy") == 0)
+                       busy = true;
+               else
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+       else if (argc < 1 || argc > 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       unsigned long duration = 0;
+       int retval = parse_ulong(args[0], &duration);
+       if (ERROR_OK != retval)
+               return retval;
+
+       if (!busy)
+       {
+               long long then = timeval_ms();
+               while (timeval_ms() - then < (long long)duration)
+               {
+                       target_call_timer_callbacks_now();
+                       usleep(1000);
+               }
+       }
+       else
+               busy_sleep(duration);
+
+       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()
 {
-       command_context_t* context = malloc(sizeof(command_context_t));
+       struct command_context* context = malloc(sizeof(struct command_context));
        extern const char startup_tcl[];
        const char *HostOs;
 
@@ -725,6 +801,8 @@ command_context_t* command_init()
        HostOs = "cygwin";
 #elif defined(__MINGW32__)
        HostOs = "mingw32";
+#elif defined(__ECOS)
+       HostOs = "ecos";
 #else
 #warn unrecognized host OS...
        HostOs = "other";
@@ -768,7 +846,7 @@ command_context_t* command_init()
        return context;
 }
 
-int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
+int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
 {
        if (!cmd_ctx)
                return ERROR_INVALID_ARGUMENTS;
@@ -777,53 +855,6 @@ int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
        return ERROR_OK;
 }
 
-/* sleep command sleeps for <n> miliseconds
- * this is useful in target startup scripts
- */
-int handle_sleep_command(struct command_context_s *cmd_ctx,
-               char *cmd, char **args, int argc)
-{
-       bool busy = false;
-       if (argc == 2)
-       {
-               if (strcmp(args[1], "busy") == 0)
-                       busy = true;
-               else
-                       return ERROR_COMMAND_SYNTAX_ERROR;
-       }
-       else if (argc < 1 || argc > 2)
-               return ERROR_COMMAND_SYNTAX_ERROR;
-
-       unsigned long duration = 0;
-       int retval = parse_ulong(args[0], &duration);
-       if (ERROR_OK != retval)
-               return retval;
-
-       if (!busy)
-       {
-               long long then = timeval_ms();
-               while (timeval_ms() - then < (long long)duration)
-               {
-                       target_call_timer_callbacks_now();
-                       usleep(1000);
-               }
-       }
-       else
-               busy_sleep(duration);
-
-       return ERROR_OK;
-}
-
-int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
-{
-       if (argc != 1)
-               return ERROR_COMMAND_SYNTAX_ERROR;
-
-       fast_and_dangerous = strcmp("enable", args[0]) == 0;
-
-       return ERROR_OK;
-}
-
 void process_jim_events(void)
 {
 #if !BUILD_ECOSBOARD
@@ -838,24 +869,15 @@ void process_jim_events(void)
 #endif
 }
 
-void register_jim(struct command_context_s *cmd_ctx, const char *name, int (*cmd)(Jim_Interp *interp, int argc, Jim_Obj *const *argv), const char *help)
+void register_jim(struct command_context *cmd_ctx, const char *name, int (*cmd)(Jim_Interp *interp, int argc, Jim_Obj *const *argv), const char *help)
 {
        Jim_CreateCommand(interp, name, cmd, NULL, NULL);
 
-       /* FIX!!! it would be prettier to invoke add_help_text...
-        * accumulate help text in Tcl helptext list.  */
-       Jim_Obj *helptext = Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
-       if (Jim_IsShared(helptext))
-               helptext = Jim_DuplicateObj(interp, helptext);
-
-       Jim_Obj *cmd_entry = Jim_NewListObj(interp, NULL, 0);
-
        Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
-       Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
+       Jim_ListAppendElement(interp, cmd_list,
+                       Jim_NewStringObj(interp, name, -1));
 
-       Jim_ListAppendElement(interp, cmd_entry, cmd_list);
-       Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
-       Jim_ListAppendElement(interp, helptext, cmd_entry);
+       command_helptext_add(cmd_list, help);
 }
 
 /* return global variable long value or 0 upon failure */