From: Quentin Young Date: Sat, 29 Oct 2016 04:43:04 +0000 (+0000) Subject: lib: Various minor improvements & bugfixes to CLI backend X-Git-Tag: frr-3.0-branchpoint~129^2~60 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=ebacb4edce50ef418e46b090288223adea89b2d0;p=mirror%2Ffrr.git lib: Various minor improvements & bugfixes to CLI backend - Do not allow tab-completion on anything except words - Rewrite cmd_make_strvec to use strsep - Remove a few trailing whitespaces - Remove cmd_complete_command_lib Signed-off-by: Quentin Young --- diff --git a/lib/command.c b/lib/command.c index f9c4107745..a12591a8bc 100644 --- a/lib/command.c +++ b/lib/command.c @@ -240,56 +240,41 @@ install_node (struct cmd_node *node, node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); } -/* Breaking up string into each command piece. I assume given - character is separated by a space character. Return value is a - vector which includes char ** data element. */ +/** + * Tokenizes a string, storing tokens in a vector. + * Whitespace is ignored. + * + * Delimiter string = " \n\r\t". + * + * @param string to tokenize + * @return tokenized string + */ vector cmd_make_strvec (const char *string) { - const char *cp, *start; - char *token; - int strlen; - vector strvec; + if (!string) return NULL; - if (string == NULL) - return NULL; + char *copy, *copystart; + copystart = copy = XSTRDUP (MTYPE_TMP, string); - cp = string; + // skip leading whitespace + while (isspace ((int) *copy) && *copy != '\0') copy++; - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - if (*cp == '!' || *cp == '#') + // if the entire string was whitespace or a comment, return + if (*copy == '\0' || *copy == '!' || *copy == '#') return NULL; - /* Prepare return vector. */ - strvec = vector_init (VECTOR_MIN_SIZE); - - /* Copy each command piece and set into vector. */ - while (1) - { - start = cp; - while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - vector_set (strvec, token); - - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; + vector strvec = vector_init (VECTOR_MIN_SIZE); + const char *delim = " \n\r\t", *tok = NULL; + while (copy) + { + tok = strsep (©, delim); + if (*tok != '\0') + vector_set (strvec, XSTRDUP (MTYPE_STRVEC, tok)); + } - if (*cp == '\0') - return strvec; - } + XFREE (MTYPE_TMP, copystart); + return strvec; } /* Free allocated string vector. */ @@ -350,7 +335,7 @@ install_element (enum node_type ntype, struct cmd_element *cmd) __func__); return; } - + cnode = vector_slot (cmdvec, ntype); if (cnode == NULL) @@ -359,17 +344,17 @@ install_element (enum node_type ntype, struct cmd_element *cmd) ntype); exit (EXIT_FAILURE); } - + if (hash_lookup (cnode->cmd_hash, cmd) != NULL) { - fprintf (stderr, + fprintf (stderr, "Multiple command installs to node %d of command:\n%s\n", ntype, cmd->string); return; } - + assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern)); - + command_parse_format (cnode->cmdgraph, cmd); vector_set (cnode->cmd_vector, cmd); @@ -570,6 +555,7 @@ completions_to_vec (struct list *completions) sizeof (void *), &compare_completions); + // make the first element, if it is present if (cr) { vector_set_index (comps, vector_active (comps), NULL); @@ -586,6 +572,7 @@ completions_to_vec (struct list *completions) * @param vline the vectorized input line * @param vty the vty with the node to match on * @param status pointer to matcher status code + * @return vector of struct cmd_token * with possible completions */ static vector cmd_complete_command_real (vector vline, struct vty *vty, int *status) @@ -658,8 +645,20 @@ cmd_describe_command (vector vline, struct vty *vty, int *status) return cmd_complete_command_real (vline, vty, status); } +/** + * Generate possible tab-completions for the given input. This function only + * returns results that would result in a valid command if used as Readline + * completions (as is the case in vtysh). For instance, if the passed vline ends + * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned. + * + * @param vline vectorized input line + * @param vty the vty + * @param status location to store matcher status code in + * @return set of valid strings for use with Readline as tab-completions. + */ + char ** -cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) +cmd_complete_command (vector vline, struct vty *vty, int *status) { char **ret = NULL; int original_node = vty->node; @@ -675,7 +674,21 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) vector_set_index (input_line, index + offset, vector_lookup (vline, index)); // get token completions -- this is a copying operation - vector comps = cmd_complete_command_real (input_line, vty, status); + vector comps, initial_comps; + initial_comps = cmd_complete_command_real (input_line, vty, status); + + // filter out everything that is not suitable for a tab-completion + comps = vector_init (VECTOR_MIN_SIZE); + for (unsigned int i = 0; i < vector_active(initial_comps); i++) + { + struct cmd_token *token = vector_slot (initial_comps, i); + if (token->type == WORD_TKN) + vector_set (comps, token); + else + del_cmd_token (token); + } + vector_free (initial_comps); + if (!MATCHER_ERROR (*status)) { // copy completions text into an array of char* @@ -688,7 +701,9 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) vector_unset (comps, i); del_cmd_token (token); } - // set the last element to NULL, which vty/vtysh uses as a sentinel value + // set the last element to NULL, because this array is used in + // a Readline completion_generator function which expects NULL + // as a sentinel value ret[i] = NULL; vector_free (comps); comps = NULL; @@ -706,12 +721,6 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) return ret; } -char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) -{ - return cmd_complete_command_lib (vline, vty, status, 0); -} - /* return parent node */ /* MUST eventually converge on CONFIG_NODE */ enum node_type @@ -1578,7 +1587,6 @@ DEFUN (config_enable_password, "Modify enable password parameters\n" "Assign the privileged level password\n" "Specifies a HIDDEN password will follow\n" - "dummy string \n" "The HIDDEN 'enable' password string\n") { int idx_8 = 2; diff --git a/lib/command.h b/lib/command.h index 4d60c5b4fe..bdf30b6f3c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -187,13 +187,10 @@ enum cmd_token_type */ struct cmd_token { - enum cmd_token_type type; // token type - + enum cmd_token_type type; // token type char *text; // token text char *desc; // token description - long long min, max; // for ranges - char *arg; // user input that matches this token }; @@ -419,7 +416,6 @@ extern void cmd_free_strvec (vector); extern char *cmd_concat_strvec (vector); extern vector cmd_describe_command (vector, struct vty *, int *status); extern char **cmd_complete_command (vector, struct vty *, int *status); -extern char **cmd_complete_command_lib (vector, struct vty *, int *status, int islib); extern const char *cmd_prompt (enum node_type); extern int command_config_read_one_line (struct vty *vty, struct cmd_element **, int use_config_node); extern int config_from_file (struct vty *, FILE *, unsigned int *line_num); diff --git a/lib/vty.c b/lib/vty.c index 78bf0e720d..53a04851d5 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -923,7 +923,7 @@ vty_complete_command (struct vty *vty) if (isspace ((int) vty->buf[vty->length - 1])) vector_set (vline, NULL); - matched = cmd_complete_command_lib (vline, vty, &ret, 1); + matched = cmd_complete_command (vline, vty, &ret); cmd_free_strvec (vline);