diff --git a/lib/command.c b/lib/command.c index c955087437..008f98a34c 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1493,6 +1493,56 @@ static void permute(struct graph_node *start, struct vty *vty) list_delete_node(position, listtail(position)); } +static void print_cmd(struct vty *vty, const char *cmd) +{ + int i, j, len = strlen(cmd); + char buf[len]; + bool skip = false; + + j = 0; + for (i = 0; i < len; i++) { + /* skip varname */ + if (cmd[i] == '$') + skip = true; + else if (strchr(" ()<>[]{}|", cmd[i])) + skip = false; + + if (skip) + continue; + + if (isspace(cmd[i])) { + /* skip leading whitespace */ + if (i == 0) + continue; + /* skip trailing whitespace */ + if (i == len - 1) + continue; + /* skip all whitespace after opening brackets or pipe */ + if (strchr("(<[{|", cmd[i - 1])) { + while (isspace(cmd[i + 1])) + i++; + continue; + } + /* skip repeated whitespace */ + if (isspace(cmd[i + 1])) + continue; + /* skip whitespace before closing brackets or pipe */ + if (strchr(")>]}|", cmd[i + 1])) + continue; + /* convert tabs to spaces */ + if (cmd[i] == '\t') { + buf[j++] = ' '; + continue; + } + } + + buf[j++] = cmd[i]; + } + buf[j] = 0; + + vty_out(vty, "%s\n", buf); +} + int cmd_list_cmds(struct vty *vty, int do_permute) { struct cmd_node *node = vector_slot(cmdvec, vty->node); @@ -1506,8 +1556,10 @@ int cmd_list_cmds(struct vty *vty, int do_permute) i++) if ((element = vector_slot(node->cmd_vector, i)) && element->attr != CMD_ATTR_DEPRECATED - && element->attr != CMD_ATTR_HIDDEN) - vty_out(vty, " %s\n", element->string); + && element->attr != CMD_ATTR_HIDDEN) { + vty_out(vty, " "); + print_cmd(vty, element->string); + } } return CMD_SUCCESS; } @@ -2240,11 +2292,7 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } -DEFUN(find, - find_cmd, - "find REGEX...", - "Find CLI command matching a regular expression\n" - "Search pattern (POSIX regex)\n") +int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc) { const struct cmd_node *node; const struct cmd_element *cli; @@ -2311,9 +2359,10 @@ DEFUN(find, for (unsigned int j = 0; j < vector_active(clis); j++) { cli = vector_slot(clis, j); - if (regexec(&exp, cli->string, 0, NULL, 0) == 0) - vty_out(vty, " (%s) %s\n", - node->name, cli->string); + if (regexec(&exp, cli->string, 0, NULL, 0) == 0) { + vty_out(vty, " (%s) ", node->name); + print_cmd(vty, cli->string); + } } } @@ -2322,6 +2371,15 @@ done: return CMD_SUCCESS; } +DEFUN(find, + find_cmd, + "find REGEX...", + "Find CLI command matching a regular expression\n" + "Search pattern (POSIX regex)\n") +{ + return cmd_find_cmds(vty, argv, argc); +} + #if defined(DEV_BUILD) && defined(HAVE_SCRIPTING) DEFUN(script, script_cmd, diff --git a/lib/command.h b/lib/command.h index c36797d125..13bd61e9fd 100644 --- a/lib/command.h +++ b/lib/command.h @@ -578,6 +578,7 @@ extern void cmd_init_config_callbacks(void (*start_config_cb)(void), extern void cmd_terminate(void); extern void cmd_exit(struct vty *vty); extern int cmd_list_cmds(struct vty *vty, int do_permute); +extern int cmd_find_cmds(struct vty *vty, struct cmd_token **argv, int argc); extern int cmd_domainname_set(const char *domainname); extern int cmd_hostname_set(const char *hostname); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index e8184c2dc8..a50dccbda6 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3729,79 +3729,7 @@ DEFUN(find, "Find CLI command matching a regular expression\n" "Search pattern (POSIX regex)\n") { - const struct cmd_node *node; - const struct cmd_element *cli; - vector clis; - regex_t exp = {}; - char *pattern = argv_concat(argv, argc, 1); - int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); - - XFREE(MTYPE_TMP, pattern); - - if (cr != 0) { - switch (cr) { - case REG_BADBR: - vty_out(vty, "%% Invalid \\{...\\} expression\n"); - break; - case REG_BADRPT: - vty_out(vty, "%% Bad repetition operator\n"); - break; - case REG_BADPAT: - vty_out(vty, "%% Regex syntax error\n"); - break; - case REG_ECOLLATE: - vty_out(vty, "%% Invalid collating element\n"); - break; - case REG_ECTYPE: - vty_out(vty, "%% Invalid character class name\n"); - break; - case REG_EESCAPE: - vty_out(vty, - "%% Regex ended with escape character (\\)\n"); - break; - case REG_ESUBREG: - vty_out(vty, - "%% Invalid number in \\digit construction\n"); - break; - case REG_EBRACK: - vty_out(vty, "%% Unbalanced square brackets\n"); - break; - case REG_EPAREN: - vty_out(vty, "%% Unbalanced parentheses\n"); - break; - case REG_EBRACE: - vty_out(vty, "%% Unbalanced braces\n"); - break; - case REG_ERANGE: - vty_out(vty, - "%% Invalid endpoint in range expression\n"); - break; - case REG_ESPACE: - vty_out(vty, "%% Failed to compile (out of memory)\n"); - break; - } - - goto done; - } - - - for (unsigned int i = 0; i < vector_active(cmdvec); i++) { - node = vector_slot(cmdvec, i); - if (!node) - continue; - clis = node->cmd_vector; - for (unsigned int j = 0; j < vector_active(clis); j++) { - cli = vector_slot(clis, j); - - if (regexec(&exp, cli->string, 0, NULL, 0) == 0) - vty_out(vty, " (%s) %s\n", - node->name, cli->string); - } - } - -done: - regfree(&exp); - return CMD_SUCCESS; + return cmd_find_cmds(vty, argv, argc); } DEFUN_HIDDEN(show_cli_graph_vtysh,