1package cli
2
3import (
4	"flag"
5	"strings"
6)
7
8type iterativeParser interface {
9	newFlagSet() (*flag.FlagSet, error)
10	useShortOptionHandling() bool
11}
12
13// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
14// iteratively catch parsing errors.  This way we achieve LR parsing without
15// transforming any arguments. Otherwise, there is no way we can discriminate
16// combined short options from common arguments that should be left untouched.
17func parseIter(ip iterativeParser, args []string) (*flag.FlagSet, error) {
18	for {
19		set, err := ip.newFlagSet()
20		if err != nil {
21			return nil, err
22		}
23
24		err = set.Parse(args)
25		if !ip.useShortOptionHandling() || err == nil {
26			return set, err
27		}
28
29		errStr := err.Error()
30		trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
31		if errStr == trimmed {
32			return nil, err
33		}
34
35		// regenerate the initial args with the split short opts
36		newArgs := []string{}
37		for i, arg := range args {
38			if arg != trimmed {
39				newArgs = append(newArgs, arg)
40				continue
41			}
42
43			shortOpts := splitShortOptions(set, trimmed)
44			if len(shortOpts) == 1 {
45				return nil, err
46			}
47
48			// add each short option and all remaining arguments
49			newArgs = append(newArgs, shortOpts...)
50			newArgs = append(newArgs, args[i+1:]...)
51			args = newArgs
52		}
53	}
54}
55
56func splitShortOptions(set *flag.FlagSet, arg string) []string {
57	shortFlagsExist := func(s string) bool {
58		for _, c := range s[1:] {
59			if f := set.Lookup(string(c)); f == nil {
60				return false
61			}
62		}
63		return true
64	}
65
66	if !isSplittable(arg) || !shortFlagsExist(arg) {
67		return []string{arg}
68	}
69
70	separated := make([]string, 0, len(arg)-1)
71	for _, flagChar := range arg[1:] {
72		separated = append(separated, "-"+string(flagChar))
73	}
74
75	return separated
76}
77
78func isSplittable(flagArg string) bool {
79	return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2
80}
81