/
mflag.go
178 lines (158 loc) · 3.76 KB
/
mflag.go
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
package unit
import (
"errors"
"fmt"
"io"
"os"
"strings"
)
// derived from https://github.com/docker/docker/blob/master/pkg/mflag/flag.go
var flagNoValue = map[string]bool{
"-rm": true, "rm": true, "t": true, "i": true,
}
// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
var ErrHelp = errors.New("flag: help requested")
// ErrRetry is the error returned if you need to try letter by letter
var ErrRetry = errors.New("flag: retry")
type FlagSet struct {
parsed bool
actual map[string]string
args []string // arguments after flags
output io.Writer // nil means stderr; use Out() accessor
env map[string]string // enviornment variables specified with -e
}
func trimQuotes(str string) string {
if len(str) == 0 {
return str
}
type quote struct {
start, end byte
}
// All valid quote types.
quotes := []quote{
// Double quotes
{
start: '"',
end: '"',
},
// Single quotes
{
start: '\'',
end: '\'',
},
}
for _, quote := range quotes {
// Only strip if outermost match.
if str[0] == quote.start && str[len(str)-1] == quote.end {
str = str[1 : len(str)-1]
break
}
}
return str
}
// Out returns the destination for usage and error messages.
func (f *FlagSet) Out() io.Writer {
if f.output == nil {
return os.Stderr
}
return f.output
}
// parseOne parses one flag. It reports whether a flag was seen.
func (f *FlagSet) parseOne() (bool, string, error) {
if len(f.args) == 0 {
return false, "", nil
}
s := f.args[0]
if len(s) == 0 || s[0] != '-' || len(s) == 1 {
return false, "", nil
}
if s[1] == '-' && len(s) == 2 { // "--" terminates the flags
f.args = f.args[1:]
return false, "", nil
}
name := s[1:]
if len(name) == 0 || name[0] == '=' {
return false, "", fmt.Errorf("bad flag syntax: %s", s)
}
// it's a flag. does it have an argument?
f.args = f.args[1:]
has_value := false
value := ""
if i := strings.Index(name, "="); i != -1 {
value = trimQuotes(name[i+1:])
has_value = true
name = name[:i]
}
// It must have a value, which might be the next argument.
if !has_value && len(f.args) > 0 && !flagNoValue[name] {
// value is the next arg
if len(f.args[0]) > 0 && f.args[0][0] != '-' {
has_value = true
value, f.args = f.args[0], f.args[1:]
}
}
// Store environment variables separately
if name == "e" || name == "-env" {
if i := strings.Index(value, "="); i == -1 {
f.env[value] = ""
} else {
f.env[value[:i]] = value[i+1:]
}
} else {
f.actual[name] = value
}
return true, "", nil
}
// Parse parses flag definitions from the argument list, which should not
// include the command name. Must be called after all flags in the FlagSet
// are defined and before flags are accessed by the program.
// The return value will be ErrHelp if -help was set but not defined.
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true
f.args = arguments
f.actual = map[string]string{}
f.env = map[string]string{}
for {
seen, name, err := f.parseOne()
if seen {
continue
}
if err == nil {
break
}
if err == ErrRetry {
if len(name) > 1 {
err = nil
for _, letter := range strings.Split(name, "") {
f.args = append([]string{"-" + letter}, f.args...)
seen2, _, err2 := f.parseOne()
if seen2 {
continue
}
if err2 != nil {
// err = fmt.Errorf("flag provided but not defined: -%s", name)
continue
break
}
}
if err == nil {
continue
}
} else {
// err = fmt.Errorf("flag provided but not defined: -%s", name)
continue
}
}
return err
}
return nil
}
func (f *FlagSet) Args() []string {
return f.args
}
func (f *FlagSet) Values() map[string]string {
return f.actual
}
func (f *FlagSet) Env() map[string]string {
return f.env
}