[ovs-dev] [RFC 2/3] ovs-vsctl-bashcomp: Add bash command-line completion for ovs-vsctl.

Alex Wang alexw at nicira.com
Fri Mar 6 02:08:21 UTC 2015


Cool, I'll adopt the change and resend patches,

On Wed, Mar 4, 2015 at 6:33 PM, Peter Amidon <peter at picnicpark.org> wrote:

> These changes all look good to me, I really like the unit32_t-as-a-stack
> idea.
>
>     ---Peter
>
> On Tue, 03 Mar 2015 21:41:11 -0800: Ben Pfaff <blp at nicira.com> wrote:
>
>
> On Mon, Feb 23, 2015 at 08:48:59AM -0800, Alex Wang wrote:
>   >> From: Peter Amidon <peter at picnicpark.org>
>   >>
>   >> This patch adds bash command-line completion script for ovs-vsctl.
>   >> Therein, codes are added to ovs-vsctl to allow it to print the
>   >> options and command arguments.  The ovs-vsctl-bashcomp.bash will
>   >> parse the vsctl command and complete on the user input.
>   >>
>   >> The completion script can do the following::
>   >>
>   >> - display available completion and complete on user input for
>   >> global/local options, command, and argument.
>   >>
>   >> - query database and expand keywords like 'table/record/column/key'
>   >> to available completions.
>   >>
>   >> - deal with argument relations like 'one and more', 'zero or one'.
>   >>
>   >> - complete multiple ovs-vsctl commands cascaded via '--'.
>   >>
>   >> To use the script, either copy it inside /etc/bash_completion.d/
>   >> or manually run it via . ovs-vsctl-bashcomp.bash.
>   >>
>   >> Signed-off-by: Alex Wang <alexw at nicira.com>
>
>   > Since I'm not a bash expert, I think I'll concentrate on the C code in
>   > ovs-vsctl.c.  Alex, have you reviewed the bash code?  As long as you
>   > have, I'm happy with it.
>
>   > valgrind reports a buffer overrun when the length of command->argument
>   > is 0.
>
>   > It appears that print_command_arguments() uses a linked list to
> maintain
>   > a stack of bools that will never get very deep.  I think a single-word
>   > bitmap would work just as well.
>
>   > Here, you can omit the multiplication by sizeof(char), because the C
>   > standard guarantees that sizeof(char) is 1:
>   >     char *simple_args = xzalloc(2 * length * sizeof(char));
>
>   > 'in_repeated' should be declared bool and assigned true and false (not
> 0
>   > and 1).
>
>   > Please write a space around binary operators, e.g.
> arguments[length-i-1]
>   > becomes arguments[length - i - 1].
>
>   > It took me a minute to figure out what was going on with the output
>   > buffer, that it's actually being written backward byte-by-byte.  That's
>   > brilliant, but I found the actual bookkeeping method difficult to
>   > follow.  This is a case where I find it a lot easier to understand when
>   > there's a pointer to the current position that we decrement as we go.
>
>   > Pretty much the same for the input buffer, although that's not quite as
>   > hard to understand at first glance.
>
>   > I think there's a missing "free" of the output buffer too.
>
>   > There are double blank lines between functions.  We don't generally do
>   > that.
>
>   > strlen(item) > 0 is more efficiently written item[0] != '\0'.
>
>   > I usually try to write "break;" after every case in a switch statement,
>   > even the last one, because it makes it harder to forget to add one if
>   > you add another case later.
>
>   > s/surronds/surrounds/ in the comment here:
>   >      * We use 'whole_word_is_optional' value to decide whether or not
> a ! or
>   >      * + should be added on encountering a space: if the optional
> surronds
>   >      * the whole word then it shouldn't be, but if it is only a part
> of the
>
>   > I got interested in this so I actually implemented these changes.
>   > Here's an incremental diff.  I verified that it produces the same
> output
>   > as the original version.
>
>   > diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
>   > index 695ecd4..efd138c 100644
>   > --- a/utilities/ovs-vsctl.c
>   > +++ b/utilities/ovs-vsctl.c
>   > @@ -768,94 +768,75 @@ static void
>   >  print_command_arguments(const struct vsctl_command_syntax *command)
>   >  {
>   >      /*
>   > -     * The argument string is parsed in reverse.  We use a stack
> 'oew_stack'
>   > -     * to keep track of nested optionals.  Whenever a ']' is
> encountered, we
>   > -     * push an element to 'oew_stack'.  The 'optional_ends_word' is
> set if
>   > -     * the ']' is not nested.  Subsequently, we pop an entry
> everytime '[' is
>   > -     * met.
>   > +     * The argument string is parsed in reverse.  We use a stack
> 'oew_stack' to
>   > +     * keep track of nested optionals.  Whenever a ']' is
> encountered, we push
>   > +     * a bit to 'oew_stack'.  The bit is set to 1 if the ']' is not
> nested.
>   > +     * Subsequently, we pop an entry everytime '[' is met.
>   >       *
>   > -     * We use 'whole_word_is_optional' value to decide whether or not
> a ! or
>   > -     * + should be added on encountering a space: if the optional
> surronds
>   > -     * the whole word then it shouldn't be, but if it is only a part
> of the
>   > -     * word (i.e. [key=]value), it should be.
>   > +     * We use 'whole_word_is_optional' value to decide whether or not
> a ! or +
>   > +     * should be added on encountering a space: if the optional
> surrounds the
>   > +     * whole word then it shouldn't be, but if it is only a part of
> the word
>   > +     * (i.e. [key=]value), it should be.
>   >       */
>   > -    struct oew_stack_element {
>   > -        bool optional_ends_word;
>   > -        struct ovs_list node;
>   > -    };
>   > +    uint32_t oew_stack = 0;
>
>   >      const char *arguments = command->arguments;
>   > -    struct ovs_list oew_stack;
>   >      int length = strlen(arguments);
>   > -    char *simple_args = xzalloc(2 * length * sizeof(char));
>   > -    /* One char has already been written: \0 */
>   > -    int chars_written = 1;
>   > -    int in_repeated = 0;
>   > -    bool whole_word_is_optional = false;
>   > -    int i;
>   > +    if (!length) {
>   > +        return;
>   > +    }
>
>   > -    list_init(&oew_stack);
>   > +    /* Output buffer, written backward from end. */
>   > +    char *output = xmalloc(2 * length);
>   > +    char *outp = output + 2 * length;
>   > +    *--outp = '\0';
>
>   > -    for (i = 1; i <= length; i++) {
>   > -        int simple_index = 2 * length - chars_written - 1;
>   > -        char current = arguments[length-i];
>   > -        struct oew_stack_element *elem;
>   > -        int oew;
>   > +    bool in_repeated = false;
>   > +    bool whole_word_is_optional = false;
>
>   > -        switch(current) {
>   > +    for (const char *inp = arguments + length; inp > arguments; ) {
>   > +        switch (*--inp) {
>   >          case ']':
>   > -            elem = xmalloc(sizeof *elem);
>   > -            if (i == 1
>   > -                || arguments[length-i+1] == ' '
>   > -                || arguments[length-i+1] == '.') {
>   > -                elem->optional_ends_word = true;
>   > -            } else {
>   > -                elem->optional_ends_word = false;
>   > +            oew_stack <<= 1;
>   > +            if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') {
>   > +                oew_stack |= 1;
>   >              }
>   > -            list_push_back(&oew_stack, &elem->node);
>   >              break;
>   >          case '[':
>   > -            elem = CONTAINER_OF(list_pop_back(&oew_stack),
>   > -                                struct oew_stack_element,
>   > -                                node);
>   > -            oew = elem->optional_ends_word;
>   > -            free(elem);
>   >              /* Checks if the whole word is optional, and sets the
>   >               * 'whole_word_is_optional' accordingly. */
>   > -            if ((i == length || arguments[length-i-1] == ' ')
>   > -                && oew) {
>   > -                simple_args[simple_index] = in_repeated ? '*' : '?';
>   > -                whole_word_is_optional = oew;
>   > +            if ((inp == arguments || inp[-1] == ' ') && oew_stack &
> 1) {
>   > +                *--outp = in_repeated ? '*' : '?';
>   > +                whole_word_is_optional = true;
>   >              } else {
>   > -                simple_args[simple_index] = '?';
>   > -                whole_word_is_optional = 0;
>   > +                *--outp = '?';
>   > +                whole_word_is_optional = false;
>   >              }
>   > -            chars_written++;
>   > +            oew_stack >>= 1;
>   >              break;
>   >          case ' ':
>   >              if (!whole_word_is_optional) {
>   > -                simple_args[simple_index] = in_repeated ? '+' : '!';
>   > -                chars_written++;
>   > +                *--outp = in_repeated ? '+' : '!';
>   >              }
>   > -            simple_args[2 * length - ++chars_written] = ' ';
>   > -            in_repeated = 0;
>   > +            *--outp = ' ';
>   > +            in_repeated = false;
>   >              whole_word_is_optional = false;
>   >              break;
>   >          case '.':
>   > -            in_repeated = 1;
>   > +            in_repeated = true;
>   >              break;
>   >          default:
>   > -            simple_args[simple_index] = current;
>   > -            chars_written++;
>   > +            *--outp = *inp;
>   > +            break;
>   >          }
>   >      }
>   > -    if (arguments[0] != '[' && chars_written > 1) {
>   > -        simple_args[2 * length - ++chars_written] = in_repeated ? '+'
> : '!';
>   > +    if (arguments[0] != '[' && outp != output + 2 * length - 1) {
>   > +        *--outp = in_repeated ? '+' : '!';
>   >      }
>   > -    printf("%s", simple_args + 2 * length - chars_written);
>   > +    printf("%s", outp);
>   > +    free(output);
>   >  }
>
>   > -
>   >  static void
>   >  print_vsctl_commands(void)
>   >  {
>   > @@ -866,10 +847,9 @@ print_vsctl_commands(void)
>   >          char *options_begin = options;
>   >          char *item;
>
>   > -        for (item = strsep(&options, ",");
>   > -             item != NULL;
>   > +        for (item = strsep(&options, ","); item != NULL;
>   >               item = strsep(&options, ",")) {
>   > -            if (strlen(item) > 0) {
>   > +            if (item[0] != '\0') {
>   >                  printf("[%s] ", item);
>   >              }
>   >          }
>   > @@ -883,7 +863,6 @@ print_vsctl_commands(void)
>   >      exit(EXIT_SUCCESS);
>   >  }
>
>   > -
>   >  static void
>   >  print_vsctl_options(const struct option *options)
>   >  {
>   > @@ -899,7 +878,6 @@ print_vsctl_options(const struct option *options)
>   >      exit(EXIT_SUCCESS);
>   >  }
>
>   > -
>   >  static char *
>   >  default_db(void)
>   >  {
>



More information about the dev mailing list