|
- 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
- }
|