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