Skip to content

Commit 809e4fb

Browse files
authored
Merge pull request urfave#2074 from dearchap/issue_2066
Fix:(issue_2066) Remove dependency on golang flag library
2 parents 706f78e + 6813d2b commit 809e4fb

23 files changed

+2650
-2145
lines changed

command.go

Lines changed: 51 additions & 815 deletions
Large diffs are not rendered by default.

command_parse.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
const (
9+
providedButNotDefinedErrMsg = "flag provided but not defined: -"
10+
argumentNotProvidedErrMsg = "flag needs an argument: "
11+
)
12+
13+
// flagFromError tries to parse a provided flag from an error message. If the
14+
// parsing fails, it returns the input error and an empty string
15+
func flagFromError(err error) (string, error) {
16+
errStr := err.Error()
17+
trimmed := strings.TrimPrefix(errStr, providedButNotDefinedErrMsg)
18+
if errStr == trimmed {
19+
return "", err
20+
}
21+
return trimmed, nil
22+
}
23+
24+
func (cmd *Command) parseFlags(args Args) (Args, error) {
25+
tracef("parsing flags from arguments %[1]q (cmd=%[2]q)", args, cmd.Name)
26+
27+
cmd.setFlags = map[Flag]struct{}{}
28+
cmd.appliedFlags = cmd.allFlags()
29+
30+
tracef("walking command lineage for persistent flags (cmd=%[1]q)", cmd.Name)
31+
32+
for pCmd := cmd.parent; pCmd != nil; pCmd = pCmd.parent {
33+
tracef(
34+
"checking ancestor command=%[1]q for persistent flags (cmd=%[2]q)",
35+
pCmd.Name, cmd.Name,
36+
)
37+
38+
for _, fl := range pCmd.Flags {
39+
flNames := fl.Names()
40+
41+
pfl, ok := fl.(LocalFlag)
42+
if !ok || pfl.IsLocal() {
43+
tracef("skipping non-persistent flag %[1]q (cmd=%[2]q)", flNames, cmd.Name)
44+
continue
45+
}
46+
47+
tracef(
48+
"checking for applying persistent flag=%[1]q pCmd=%[2]q (cmd=%[3]q)",
49+
flNames, pCmd.Name, cmd.Name,
50+
)
51+
52+
applyPersistentFlag := true
53+
54+
for _, name := range flNames {
55+
if cmd.lFlag(name) != nil {
56+
applyPersistentFlag = false
57+
break
58+
}
59+
}
60+
61+
if !applyPersistentFlag {
62+
tracef("not applying as persistent flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name)
63+
continue
64+
}
65+
66+
tracef("applying as persistent flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name)
67+
68+
tracef("appending to applied flags flag=%[1]q (cmd=%[2]q)", flNames, cmd.Name)
69+
cmd.appliedFlags = append(cmd.appliedFlags, fl)
70+
}
71+
}
72+
73+
tracef("parsing flags iteratively tail=%[1]q (cmd=%[2]q)", args.Tail(), cmd.Name)
74+
defer tracef("done parsing flags (cmd=%[1]q)", cmd.Name)
75+
76+
posArgs := []string{}
77+
for rargs := args.Slice(); len(rargs) > 0; rargs = rargs[1:] {
78+
tracef("rearrange:1 (cmd=%[1]q) %[2]q", cmd.Name, rargs)
79+
80+
firstArg := strings.TrimSpace(rargs[0])
81+
if len(firstArg) == 0 {
82+
break
83+
}
84+
85+
// stop parsing once we see a "--"
86+
if firstArg == "--" {
87+
posArgs = append(posArgs, rargs[1:]...)
88+
return &stringSliceArgs{posArgs}, nil
89+
}
90+
91+
// handle positional args
92+
if firstArg[0] != '-' {
93+
// positional argument probably
94+
tracef("rearrange-3 (cmd=%[1]q) check %[2]q", cmd.Name, firstArg)
95+
96+
// if there is a command by that name let the command handle the
97+
// rest of the parsing
98+
if cmd.Command(firstArg) != nil {
99+
posArgs = append(posArgs, rargs...)
100+
return &stringSliceArgs{posArgs}, nil
101+
}
102+
103+
posArgs = append(posArgs, firstArg)
104+
continue
105+
}
106+
107+
numMinuses := 1
108+
// this is same as firstArg == "-"
109+
if len(firstArg) == 1 {
110+
posArgs = append(posArgs, firstArg)
111+
break
112+
}
113+
114+
shortOptionHandling := cmd.useShortOptionHandling()
115+
116+
// stop parsing -- as short flags
117+
if firstArg[1] == '-' {
118+
numMinuses++
119+
shortOptionHandling = false
120+
}
121+
122+
tracef("parseFlags (shortOptionHandling=%[1]q)", shortOptionHandling)
123+
124+
flagName := firstArg[numMinuses:]
125+
flagVal := ""
126+
tracef("flagName:1 (fName=%[1]q)", flagName)
127+
if index := strings.Index(flagName, "="); index != -1 {
128+
flagVal = flagName[index+1:]
129+
flagName = flagName[:index]
130+
}
131+
132+
tracef("flagName:2 (fName=%[1]q) (fVal=%[2]q)", flagName, flagVal)
133+
134+
f := cmd.lookupFlag(flagName)
135+
// found a flag matching given flagName
136+
if f != nil {
137+
tracef("Trying flag type (fName=%[1]q) (type=%[2]T)", flagName, f)
138+
if fb, ok := f.(boolFlag); ok && fb.IsBoolFlag() {
139+
if flagVal == "" {
140+
flagVal = "true"
141+
}
142+
tracef("parse Apply bool flag (fName=%[1]q) (fVal=%[2]q)", flagName, flagVal)
143+
if err := cmd.set(flagName, f, flagVal); err != nil {
144+
return &stringSliceArgs{posArgs}, err
145+
}
146+
continue
147+
}
148+
149+
tracef("processing non bool flag (fName=%[1]q)", flagName)
150+
// not a bool flag so need to get the next arg
151+
if flagVal == "" {
152+
if len(rargs) == 1 {
153+
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, firstArg)
154+
}
155+
flagVal = rargs[1]
156+
rargs = rargs[1:]
157+
}
158+
159+
tracef("setting non bool flag (fName=%[1]q) (fVal=%[2]q)", flagName, flagVal)
160+
if err := cmd.set(flagName, f, flagVal); err != nil {
161+
return &stringSliceArgs{posArgs}, err
162+
}
163+
164+
continue
165+
}
166+
167+
// no flag lookup found and short handling is disabled
168+
if !shortOptionHandling {
169+
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", providedButNotDefinedErrMsg, flagName)
170+
}
171+
172+
// try to split the flags
173+
for index, c := range flagName {
174+
tracef("processing flag (fName=%[1]q)", string(c))
175+
if sf := cmd.lookupFlag(string(c)); sf == nil {
176+
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", providedButNotDefinedErrMsg, flagName)
177+
} else if fb, ok := sf.(boolFlag); ok && fb.IsBoolFlag() {
178+
fv := flagVal
179+
if index == (len(flagName)-1) && flagVal == "" {
180+
fv = "true"
181+
}
182+
if fv == "" {
183+
fv = "true"
184+
}
185+
if err := cmd.set(flagName, sf, fv); err != nil {
186+
tracef("processing flag.2 (fName=%[1]q)", string(c))
187+
return &stringSliceArgs{posArgs}, err
188+
}
189+
} else if index == len(flagName)-1 { // last flag can take an arg
190+
if flagVal == "" {
191+
if len(rargs) == 1 {
192+
return &stringSliceArgs{posArgs}, fmt.Errorf("%s%s", argumentNotProvidedErrMsg, string(c))
193+
}
194+
flagVal = rargs[1]
195+
}
196+
tracef("parseFlags (flagName %[1]q) (flagVal %[2]q)", flagName, flagVal)
197+
if err := cmd.set(flagName, sf, flagVal); err != nil {
198+
tracef("processing flag.4 (fName=%[1]q)", string(c))
199+
return &stringSliceArgs{posArgs}, err
200+
}
201+
}
202+
}
203+
}
204+
205+
tracef("returning-2 (cmd=%[1]q) args %[2]q", cmd.Name, posArgs)
206+
return &stringSliceArgs{posArgs}, nil
207+
}

0 commit comments

Comments
 (0)