This is an automated email from Gerrit.

Antonio Borneo ([email protected]) just uploaded a new patch set to 
Gerrit, which you can find at http://openocd.zylin.com/5670

-- gerrit

commit eb69a70ebe0bcd40ee9fbac78a7ffc3d95006d5b
Author: Antonio Borneo <[email protected]>
Date:   Tue May 12 17:41:22 2020 +0200

    help: move help functionality from C to tcl
    
    The current implementation of "help" related commands is tightly
    connected to the tree of struct command.
    
    Re-implement it from scratch in tcl, by keeping the same command
    names and producing the same output (tested on all the help and
    usage strings in current master branch).
    
    The old implementation was broken while checking the line limit of
    75 chars (e.g. "usage load_image"), thus line wrapping was not
    correct. The tcl implementation strictly follows the line size.
    
    Dedicated commands "del_help_text" and "del_usage_text" are added
    to handle command unregistration. Before was enough ripping
    commands away from the tree of struct command to remove the
    associated help.
    
    Move the registration of the initial openocd commands after the
    evaluation of startup.tcl, so help tcl functionality is already
    available to track the new commands.
    
    Change-Id: Ifd37bb5bd374cba1a22cd7aac208505b4ae1e6fc
    Signed-off-by: Antonio Borneo <[email protected]>

diff --git a/src/helper/command.c b/src/helper/command.c
index 9d75411..dab1d90 100644
--- a/src/helper/command.c
+++ b/src/helper/command.c
@@ -259,8 +259,6 @@ static void command_free(struct command *c)
        }
 
        free(c->name);
-       free(c->help);
-       free(c->usage);
        free(c);
 }
 
@@ -289,12 +287,7 @@ static struct command *command_new(struct command_context 
*cmd_ctx,
                return NULL;
 
        c->name = strdup(cr->name);
-       if (cr->help)
-               c->help = strdup(cr->help);
-       if (cr->usage)
-               c->usage = strdup(cr->usage);
-
-       if (!c->name || (cr->help && !c->help) || (cr->usage && !c->usage))
+       if (!c->name)
                goto command_new_error;
 
        c->parent = parent;
@@ -304,6 +297,19 @@ static struct command *command_new(struct command_context 
*cmd_ctx,
 
        command_add_child(command_list_for_parent(cmd_ctx, parent), c);
 
+       char *full_name = NULL;
+
+       if (cr->help || cr->usage)
+               full_name = command_name(c, ' ');
+
+       if (cr->help)
+               command_run_linef(cmd_ctx, "add_help_text {%s} {%s}", 
full_name, cr->help);
+
+       if (cr->usage)
+               command_run_linef(cmd_ctx, "add_usage_text {%s} {%s}", 
full_name, cr->usage);
+
+       free(full_name);
+
        return c;
 
 command_new_error:
@@ -428,6 +434,13 @@ static int unregister_command(struct command_context 
*context,
                if (strcmp(name, c->name) != 0)
                        continue;
 
+               char *full_name = command_name(c, ' ');
+
+               command_run_linef(context, "del_help_text {%s}", full_name);
+               command_run_linef(context, "del_usage_text {%s}", full_name);
+
+               free(full_name);
+
                if (p)
                        p->next = c->next;
                else
@@ -749,164 +762,6 @@ 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_COMMAND_SYNTAX_ERROR;
-       *out = command_find(head, CMD_ARGV[0]);
-       if (NULL == *out)
-               return ERROR_COMMAND_SYNTAX_ERROR;
-       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,
-       bool show_help, const char *cmd_match);
-
-static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n,
-       bool show_help, const char *cmd_match)
-{
-       for (struct command *c = head; NULL != c; c = c->next)
-               CALL_COMMAND_HANDLER(command_help_show, c, n, show_help, 
cmd_match);
-       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("%.*s", (int)(cp - last), last);
-               last = cp + 1;
-               n = n2;
-       }
-}
-
-static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
-       bool show_help, const char *cmd_match)
-{
-       char *cmd_name = command_name(c, ' ');
-       if (NULL == cmd_name)
-               return ERROR_FAIL;
-
-       /* If the match string occurs anywhere, we print out
-        * stuff for this command. */
-       bool is_match = (strstr(cmd_name, cmd_match) != NULL) ||
-               ((c->usage != NULL) && (strstr(c->usage, cmd_match) != NULL)) ||
-               ((c->help != NULL) && (strstr(c->help, cmd_match) != NULL));
-
-       if (is_match) {
-               command_help_show_indent(n);
-               LOG_USER_N("%s", cmd_name);
-       }
-       free(cmd_name);
-
-       if (is_match) {
-               if (c->usage && strlen(c->usage) > 0) {
-                       LOG_USER_N(" ");
-                       command_help_show_wrap(c->usage, 0, n + 5);
-               } else
-                       LOG_USER_N("\n");
-       }
-
-       if (is_match && show_help) {
-               char *msg;
-
-               /* Normal commands are runtime-only; highlight exceptions */
-               if (c->mode != COMMAND_EXEC) {
-                       const char *stage_msg = "";
-
-                       switch (c->mode) {
-                               case COMMAND_CONFIG:
-                                       stage_msg = " (configuration command)";
-                                       break;
-                               case COMMAND_ANY:
-                                       stage_msg = " (command valid any time)";
-                                       break;
-                               default:
-                                       stage_msg = " (?mode error?)";
-                                       break;
-                       }
-                       msg = alloc_printf("%s%s", c->help ? : "", stage_msg);
-               } else
-                       msg = alloc_printf("%s", c->help ? : "");
-
-               if (NULL != msg) {
-                       command_help_show_wrap(msg, n + 3, n + 3);
-                       free(msg);
-               } else
-                       return -ENOMEM;
-       }
-
-       if (++n > 5) {
-               LOG_ERROR("command recursion exceeded");
-               return ERROR_FAIL;
-       }
-
-       return CALL_COMMAND_HANDLER(command_help_show_list,
-               c->children, n, show_help, cmd_match);
-}
-
-COMMAND_HANDLER(handle_help_command)
-{
-       bool full = strcmp(CMD_NAME, "help") == 0;
-       int retval;
-       struct command *c = CMD_CTX->commands;
-       char *cmd_match = NULL;
-
-       if (CMD_ARGC == 0)
-               cmd_match = "";
-       else if (CMD_ARGC >= 1) {
-               unsigned i;
-
-               for (i = 0; i < CMD_ARGC; ++i) {
-                       if (NULL != cmd_match) {
-                               char *prev = cmd_match;
-
-                               cmd_match = alloc_printf("%s %s", cmd_match, 
CMD_ARGV[i]);
-                               free(prev);
-                               if (NULL == cmd_match) {
-                                       LOG_ERROR("unable to build search 
string");
-                                       return -ENOMEM;
-                               }
-                       } else {
-                               cmd_match = alloc_printf("%s", CMD_ARGV[i]);
-                               if (NULL == cmd_match) {
-                                       LOG_ERROR("unable to build search 
string");
-                                       return -ENOMEM;
-                               }
-                       }
-               }
-       } else
-               return ERROR_COMMAND_SYNTAX_ERROR;
-
-       retval = CALL_COMMAND_HANDLER(command_help_show_list,
-                       c, 0, full, cmd_match);
-
-       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)
 {
@@ -1095,83 +950,6 @@ static int jim_command_mode(Jim_Interp *interp, int argc, 
Jim_Obj *const *argv)
        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) {
-                       if (*nc->usage)
-                               replaced = true;
-                       free(nc->usage);
-               }
-               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;
-       }
-
-       /* save help text and remove it from argument list */
-       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_COMMAND_SYNTAX_ERROR;
-       }
-       /* likewise for the leaf command name */
-       const char *cmd_name = CMD_ARGV[--CMD_ARGC];
-
-       struct command *c = NULL;
-       if (CMD_ARGC > 0) {
-               c = CMD_CTX->commands;
-               int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
-               if (ERROR_OK != retval)
-                       return retval;
-       }
-       return help_add_command(CMD_CTX, c, cmd_name, help, usage);
-}
-
 /* sleep command sleeps for <n> milliseconds
  * this is useful in target startup scripts
  */
@@ -1244,22 +1022,6 @@ static const struct command_registration 
command_builtin_handlers[] = {
                .usage = "[-n] string",
        },
        {
-               .name = "add_help_text",
-               .handler = handle_help_add_command,
-               .mode = COMMAND_ANY,
-               .help = "Add new command help text; "
-                       "Command can be multiple tokens.",
-               .usage = "command_name helptext_string",
-       },
-       {
-               .name = "add_usage_text",
-               .handler = handle_help_add_command,
-               .mode = COMMAND_ANY,
-               .help = "Add new command usage text; "
-                       "command can be multiple tokens.",
-               .usage = "command_name usage_string",
-       },
-       {
                .name = "sleep",
                .handler = handle_sleep_command,
                .mode = COMMAND_ANY,
@@ -1268,22 +1030,6 @@ static const struct command_registration 
command_builtin_handlers[] = {
                .usage = "milliseconds ['busy']",
        },
        {
-               .name = "help",
-               .handler = handle_help_command,
-               .mode = COMMAND_ANY,
-               .help = "Show full command help; "
-                       "command can be multiple tokens.",
-               .usage = "[command_name]",
-       },
-       {
-               .name = "usage",
-               .handler = handle_help_command,
-               .mode = COMMAND_ANY,
-               .help = "Show basic command usage; "
-                       "command can be multiple tokens.",
-               .usage = "[command_name]",
-       },
-       {
                .name = "command",
                .mode = COMMAND_ANY,
                .help = "core command group (introspection)",
@@ -1344,8 +1090,6 @@ struct command_context *command_init(const char 
*startup_tcl, Jim_Interp *interp
        Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
                Jim_NewStringObj(interp, HostOs, strlen(HostOs)));
 
-       register_commands(context, NULL, command_builtin_handlers);
-
        Jim_SetAssocData(interp, "context", NULL, context);
        if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl", 1) == 
JIM_ERR) {
                LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
@@ -1353,6 +1097,7 @@ struct command_context *command_init(const char 
*startup_tcl, Jim_Interp *interp
                LOG_USER_N("%s", Jim_GetString(Jim_GetResult(interp), NULL));
                exit(-1);
        }
+       register_commands(context, NULL, command_builtin_handlers);
        Jim_DeleteAssocData(interp, "context");
 
        return context;
diff --git a/src/helper/command.h b/src/helper/command.h
index 008dce5..84ff029 100644
--- a/src/helper/command.h
+++ b/src/helper/command.h
@@ -179,8 +179,6 @@ typedef __COMMAND_HANDLER((*command_handler_t));
 
 struct command {
        char *name;
-       char *help;
-       char *usage;
        struct command *parent;
        struct command *children;
        command_handler_t handler;
diff --git a/src/helper/startup.tcl b/src/helper/startup.tcl
index 71f489d..db8a736 100644
--- a/src/helper/startup.tcl
+++ b/src/helper/startup.tcl
@@ -3,6 +3,149 @@
 # Embedded into OpenOCD executable
 #
 
+# handle commands help and usage
+namespace eval openocd_help {
+       namespace export help usage add_help_text del_help_text add_usage_text 
del_usage_text
+       variable help_array {}
+       variable usage_array {}
+       variable line_width 75
+
+       proc add_help_text {command_name helptext_string} {
+               variable help_array
+
+               set help_array($command_name) ${helptext_string}
+               return {}
+       }
+
+       proc del_help_text {command_name} {
+               variable help_array
+
+               unset -nocomplain help_array($command_name)
+               return {}
+       }
+
+       proc add_usage_text {command_name usagetext_string} {
+               variable usage_array
+
+               set usage_array($command_name) ${usagetext_string}
+               return {}
+       }
+
+       proc del_usage_text {command_name} {
+               variable usage_array
+
+               unset -nocomplain usage_array($command_name)
+               return {}
+       }
+
+       proc search_token {token} {
+               variable help_array
+               variable usage_array
+               set result {}
+
+               if {${token} eq ""} {
+                       return [lsort -unique [concat [array names help_array] 
[array names usage_array]]]
+               }
+
+               foreach key [array names help_array] {
+                       if {[string first ${token} ${key}] != -1} {lappend 
result ${key}; continue}
+                       if {[string first ${token} $help_array(${key})] != -1} 
{lappend result ${key}}
+               }
+               foreach key [array names usage_array] {
+                       if {[string first ${token} ${key}] != -1} {lappend 
result ${key}; continue}
+                       if {[string first ${token} $usage_array(${key})] != -1} 
{lappend result ${key}}
+               }
+
+               return [lsort -unique ${result}]
+       }
+
+       # Wrap "text" to fit in lines of "line_length" characters.
+       # Add "first_line_indent" spaces in front of first line and
+       # "left_indent" spaces in front of the remaining lines.
+       # Wrap only happens at space character.
+       # Then echo the result.
+
+       proc wrap_echo {first_line_indent left_indent text} {
+               variable line_width
+
+               # Find where to split 1st line
+               set text [string repeat " " $first_line_indent]${text}
+               regexp -indices ".{1,$line_width} " ${text}\x20 position
+               set position [lindex $position 1]
+
+               set splitter \n[string repeat " " $left_indent]\\1
+               set new_width [expr $line_width - $left_indent]
+               # Split all lines
+               echo [regsub -all -start $position " *(.{1,$new_width})( +|$)" 
${text} $splitter]
+       }
+
+       proc help {args} {
+               variable help_array
+               variable usage_array
+
+               foreach cmd [search_token [eval concat $args]] {
+                       set subcmds_n [regexp -all " " $cmd]
+                       set usage_indent_1 [expr 2 * $subcmds_n]
+                       set usage_indent_2 [expr $usage_indent_1 + 10]
+                       set help_indent [expr $usage_indent_1 + 6]
+
+                       if {[info exists usage_array($cmd)] && 
$usage_array($cmd) ne ""} {
+                               wrap_echo $usage_indent_1 $usage_indent_2 
${cmd}\x20$usage_array(${cmd})
+                       } else {
+                               wrap_echo $usage_indent_1 $usage_indent_2 $cmd
+                       }
+
+                       set help_text ""
+                       if {[info exists help_array($cmd)] && $help_array($cmd) 
ne ""} {
+                               set help_text $help_array($cmd)
+                       }
+                       switch [eval command mode $cmd] {
+                               "any"    {set help_text [concat ${help_text} 
(command valid any time)]}
+                               "config" {set help_text [concat ${help_text} 
(configuration command)]}
+                               default  {}
+                       }
+                       if {$help_text ne ""} {
+                               wrap_echo $help_indent $help_indent ${help_text}
+                       }
+               }
+       }
+
+       proc usage {args} {
+               variable usage_array
+
+               foreach cmd [search_token [eval concat $args]] {
+                       set subcmds_n [regexp -all " " $cmd]
+                       set usage_indent_1 [expr 2 * $subcmds_n]
+                       set usage_indent_2 [expr $usage_indent_1 + 10]
+
+                       if {[info exists usage_array($cmd)] && 
$usage_array($cmd) ne ""} {
+                               wrap_echo $usage_indent_1 $usage_indent_2 "$cmd 
$usage_array($cmd)"
+                       } else {
+                               wrap_echo $usage_indent_1 $usage_indent_2 $cmd
+                       }
+               }
+       }
+}
+namespace import openocd_help::help openocd_help::usage openocd_help::add_* 
openocd_help::del_*
+
+add_help_text help "Show full command help; command can be multiple tokens."
+add_usage_text help {[command_name]}
+
+add_help_text usage "Show basic command usage; command can be multiple tokens."
+add_usage_text usage {[command_name]}
+
+add_help_text add_help_text "Add new command help text; Command can be 
multiple tokens."
+add_usage_text add_help_text "command_name helptext_string"
+
+add_help_text del_help_text "Delete a command help text; Command can be 
multiple tokens."
+add_usage_text del_help_text "command_name"
+
+add_help_text add_usage_text "Add new command usage text; command can be 
multiple tokens."
+add_usage_text add_usage_text "command_name usage_string"
+
+add_help_text del_usage_text "Delete a command usage text; command can be 
multiple tokens."
+add_usage_text del_usage_text "command_name"
+
 # Try flipping / and \ to find file if the filename does not
 # match the precise spelling
 proc find {filename} {

-- 


_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to