-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgetopt.go
155 lines (147 loc) · 3.36 KB
/
getopt.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
// getopt is a POSIX-compatible implementation of getopt(3) for Go.
//
// Example usage:
//
// import (
// "os"
// "git.sr.ht/~sircmpwn/getopt"
// )
//
// func main() {
// opts, optind, err := getopt.Getopts(os.Args, "abc:d:")
// if err != nil {
// panic(err)
// }
// for _, opt := range opts {
// switch opt.Option {
// case 'a':
// println("Option -a specified")
// case 'b':
// println("Option -b specified")
// case 'c':
// println("Option -c specified: " + opt.Value)
// case 'd':
// println("Option -d specified: " + opt.Value)
// }
// }
// println("Remaining arguments:")
// for _, arg := range os.Args[optind:] {
// println(arg)
// }
// }
//
// A flag[0]-like interface is also supported.
//
// import (
// "git.sr.ht/~sircmpwn/getopt"
// )
//
// func main() {
// a := getopt.Bool("a", false, "turn on option a")
// b := getopt.Int("b", 1, "set b to a numerical value")
// var opt string
// getopt.StringVar(&opt, "c", "", "let c be specified string")
//
// err := getopt.Parse()
// if err != nil {
// panic(err)
// }
//
// print("Value of a: ")
// println(*a)
// print("Value of b: ")
// println(*b)
// println("Value of c: " + opt)
//
// println("Remaining arguments:")
// for _, arg := range getopt.Args() {
// println(arg)
// }
// }
//
// [0]: https://golang.org/pkg/flag/
package getopt
import (
"fmt"
"os"
)
// In the case of "-o example", Option is 'o' and "example" is Value. For
// options which do not take an argument, Value is "".
type Option struct {
Option rune
Value string
}
// This is returned when an unknown option is found in argv, but not in the
// option spec.
type UnknownOptionError rune
func (e UnknownOptionError) Error() string {
return fmt.Sprintf("%s: unknown option -%c", os.Args[0], rune(e))
}
// This is returned when an option with a mandatory argument is missing that
// argument.
type MissingOptionError rune
func (e MissingOptionError) Error() string {
return fmt.Sprintf("%s: expected argument for -%c", os.Args[0], rune(e))
}
// Getopts implements a POSIX-compatible options interface.
//
// Returns a slice of options and the index of the first non-option argument.
//
// If an error is returned, you must print it to stderr to be POSIX complaint.
func Getopts(argv []string, spec string) ([]Option, int, error) {
optmap := make(map[rune]bool)
runes := []rune(spec)
for i, rn := range spec {
if rn == ':' {
if i == 0 {
continue
}
optmap[runes[i-1]] = true
} else {
optmap[rn] = false
}
}
var (
i int
opts []Option
)
for i = 1; i < len(argv); i++ {
arg := argv[i]
runes = []rune(arg)
if len(arg) == 0 || arg == "-" {
break
}
if arg[0] != '-' {
break
}
if arg == "--" {
i++
break
}
for j, opt := range runes[1:] {
if optopt, ok := optmap[opt]; !ok {
opts = append(opts, Option{'?', ""})
return opts, i, UnknownOptionError(opt)
} else if optopt {
if j+1 < len(runes)-1 {
opts = append(opts, Option{opt, string(runes[j+2:])})
break
} else {
if i+1 >= len(argv) {
if len(spec) >= 1 && spec[0] == ':' {
opts = append(opts, Option{':', string(opt)})
} else {
return opts, i, MissingOptionError(opt)
}
} else {
opts = append(opts, Option{opt, argv[i+1]})
i++
}
}
} else {
opts = append(opts, Option{opt, ""})
}
}
}
return opts, i, nil
}