From ca3fdc5252263f5b80b9eae023e9253fb55de0bb Mon Sep 17 00:00:00 2001 From: Adam Chalkley Date: Thu, 10 Oct 2019 12:58:50 -0500 Subject: [PATCH 1/3] README: Fix small typo in example description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96720b6..246dd5c 100644 --- a/README.md +++ b/README.md @@ -691,7 +691,7 @@ The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context. It will then use that file name to initialize the yaml input source for any flags that are defined on that command. As a note the "load" flag used would also have to be defined on the command flags in order -for this code snipped to work. +for this code snippet to work. Currently only YAML, JSON, and TOML files are supported but developers can add support for other input sources by implementing the altsrc.InputSourceContext for their From df740f656281131043563a9a3b92f83a2e495fa7 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Fri, 11 Oct 2019 22:32:21 -0400 Subject: [PATCH 2/3] Fix infinite loop flag parsing with short options --- command_test.go | 5 +++++ parse.go | 17 ++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/command_test.go b/command_test.go index 6235b41..66859c0 100644 --- a/command_test.go +++ b/command_test.go @@ -70,8 +70,13 @@ func TestParseAndRunShortOpts(t *testing.T) { {[]string{"foo", "test", "-af"}, nil, []string{}}, {[]string{"foo", "test", "-cf"}, nil, []string{}}, {[]string{"foo", "test", "-acf"}, nil, []string{}}, + {[]string{"foo", "test", "--acf"}, errors.New("flag provided but not defined: -acf"), nil}, {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, + {[]string{"foo", "test", "-acf", "-invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, + {[]string{"foo", "test", "--invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, + {[]string{"foo", "test", "-acf", "--invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, + {[]string{"foo", "test", "-acf", "arg1", "--invalid"}, nil, []string{"arg1", "--invalid"}}, {[]string{"foo", "test", "-acfi", "not-arg", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, {[]string{"foo", "test", "-i", "ivalue"}, nil, []string{}}, {[]string{"foo", "test", "-i", "ivalue", "arg1"}, nil, []string{"arg1"}}, diff --git a/parse.go b/parse.go index 2c2005c..cab99e5 100644 --- a/parse.go +++ b/parse.go @@ -22,28 +22,27 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { } errStr := err.Error() - trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ") + trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") if errStr == trimmed { return err } // regenerate the initial args with the split short opts - newArgs := []string{} for i, arg := range args { - if arg != trimmed { - newArgs = append(newArgs, arg) + // skip args that are not part of the error message + if name := strings.TrimLeft(arg, "-"); name != trimmed { continue } - shortOpts := splitShortOptions(set, trimmed) + // if we can't split, the error was accurate + shortOpts := splitShortOptions(set, arg) if len(shortOpts) == 1 { return err } - // add each short option and all remaining arguments - newArgs = append(newArgs, shortOpts...) - newArgs = append(newArgs, args[i+1:]...) - args = newArgs + // swap current argument with the split version + args = append(args[:i], append(shortOpts, args[i+1:]...)...) + break } // Since custom parsing failed, replace the flag set before retrying From f5306b622f216cafde2a670b9a9e620cb6b0fad1 Mon Sep 17 00:00:00 2001 From: Robert Liebowitz Date: Fri, 11 Oct 2019 22:57:55 -0400 Subject: [PATCH 3/3] Ensure infinite loop cannot occur in parsing --- parse.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/parse.go b/parse.go index cab99e5..660f538 100644 --- a/parse.go +++ b/parse.go @@ -28,6 +28,7 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { } // regenerate the initial args with the split short opts + argsWereSplit := false for i, arg := range args { // skip args that are not part of the error message if name := strings.TrimLeft(arg, "-"); name != trimmed { @@ -42,9 +43,16 @@ func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { // swap current argument with the split version args = append(args[:i], append(shortOpts, args[i+1:]...)...) + argsWereSplit = true break } + // This should be an impossible to reach code path, but in case the arg + // splitting failed to happen, this will prevent infinite loops + if !argsWereSplit { + return err + } + // Since custom parsing failed, replace the flag set before retrying newSet, err := ip.newFlagSet() if err != nil {