c677518065
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"flag"
|
|
"strings"
|
|
)
|
|
|
|
type iterativeParser interface {
|
|
newFlagSet() (*flag.FlagSet, error)
|
|
useShortOptionHandling() bool
|
|
}
|
|
|
|
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
|
|
// iteratively catch parsing errors. This way we achieve LR parsing without
|
|
// transforming any arguments. Otherwise, there is no way we can discriminate
|
|
// combined short options from common arguments that should be left untouched.
|
|
func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) {
|
|
for {
|
|
set, err := ip.newFlagSet()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = set.Parse(args)
|
|
if !ip.useShortOptionHandling() || err == nil {
|
|
return set, err
|
|
}
|
|
|
|
errStr := err.Error()
|
|
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
|
|
if errStr == trimmed {
|
|
return nil, err
|
|
}
|
|
|
|
// regenerate the initial args with the split short opts
|
|
newArgs := []string{}
|
|
for i, arg := range args {
|
|
if arg != trimmed {
|
|
newArgs = append(newArgs, arg)
|
|
continue
|
|
}
|
|
|
|
shortOpts := splitShortOptions(set, trimmed)
|
|
if len(shortOpts) == 1 {
|
|
return nil, err
|
|
}
|
|
|
|
// add each short option and all remaining arguments
|
|
newArgs = append(newArgs, shortOpts...)
|
|
newArgs = append(newArgs, args[i+1:]...)
|
|
args = newArgs
|
|
}
|
|
}
|
|
}
|
|
|
|
func splitShortOptions(set *flag.FlagSet, arg string) []string {
|
|
shortFlagsExist := func(s string) bool {
|
|
for _, c := range s[1:] {
|
|
if f := set.Lookup(string(c)); f == nil {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
if !isSplittable(arg) || !shortFlagsExist(arg) {
|
|
return []string{arg}
|
|
}
|
|
|
|
separated := make([]string, 0, len(arg)-1)
|
|
for _, flagChar := range arg[1:] {
|
|
separated = append(separated, "-"+string(flagChar))
|
|
}
|
|
|
|
return separated
|
|
}
|
|
|
|
func isSplittable(flagArg string) bool {
|
|
return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2
|
|
}
|