This repository has been archived by the owner on Jun 27, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 218
/
flag.go
114 lines (96 loc) · 2.76 KB
/
flag.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
package flag
import (
"flag"
)
// FilterArgs filters the args slice to only include the the flags
// in the given flagset and returns a new arg slice that has the
// included args as well as a slice that has only the excluded args.
// The final returned slice is the positional arguments.
func FilterArgs(fs *flag.FlagSet, args []string) ([]string, []string, []string) {
// Optimistically make all the length of the arguments. There
// should never be so many arguments that this is too ineffecient.
inc := make([]string, 0, len(args))
exc := make([]string, 0, len(args))
pos := make([]string, 0, len(args))
// Make a map of the valid flags
flags := make(map[string]struct{})
fs.VisitAll(func(f *flag.Flag) {
flags[f.Name] = struct{}{}
})
// Go through each, parse out a single argument, and determine where
// it should go plus how many of the slots.
i := 0
for i < len(args) {
n, loc := filterOne(flags, args, i)
// Determine what slice to add the values to
var result *[]string
switch loc {
case filterLocBoth:
result = &pos
case filterLocInc:
result = &inc
case filterLocExc:
result = &exc
}
// Copy the values
*result = append(*result, args[i:i+n]...)
// Increment i so we continue moving through the arguments
i += n
}
return inc, exc, pos
}
type filterLoc byte
const (
filterLocBoth filterLoc = iota
filterLocInc
filterLocExc
)
// filterOne is based very heavily on the official flag package
// "parseOne" function. We do this on purpose so that we parse things
// as similarly as possible in order to split the args.
func filterOne(flags map[string]struct{}, args []string, i int) (int, filterLoc) {
// Get the arg
s := args[i]
if s == "-h" || s == "--help" {
return 1, filterLocInc
}
// If the arg is empty, not a flag, or just a "-" then we have to
// add it to BOTH lists.
if len(s) == 0 || s[0] != '-' || len(s) == 1 {
return 1, filterLocBoth
}
// If we hit double minuses, then we return the rest of the args to
// BOTH lists.
num_minuses := 1
if s[1] == '-' {
num_minuses++
if len(s) == 2 { // "--" terminates the flags
return len(args) - i, filterLocBoth
}
}
// Otherwise, get the name. If the syntax is invalid, let's just add it
// to both.
name := s[num_minuses:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
return 1, filterLocBoth
}
// Check for an argument to the flag
has_value := false
for i := 1; i < len(name); i++ { // equals cannot be first
if name[i] == '=' {
has_value = true
name = name[0:i]
break
}
}
// Determine where this will go from here on out
pos := filterLocInc
if _, valid := flags[name]; !valid {
pos = filterLocExc
}
// It must have a value, which might be the next argument.
if !has_value && len(args) > i+1 {
return 2, pos
}
return 1, pos
}