PageRenderTime 43ms CodeModel.GetById 12ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/opts.go

https://code.google.com/p/opts-go/
Go | 409 lines | 291 code | 48 blank | 70 comment | 50 complexity | fa793b30b054dccdc54951798c2dcd01 MD5 | raw file
  1// Copyright 2010 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5/*
  6The opts package provides advanced GNU- and POSIX- style option parsing.
  7*/
  8package opts
  9
 10import (
 11	"fmt"
 12	"os"
 13	"strings"
 14)
 15
 16//
 17// Exported variables
 18//
 19
 20// The name with which this program was called
 21var Xname = os.Args[0]
 22
 23// The list of optionless arguments provided
 24var Args []string = make([]string, 0, len(os.Args)-1)
 25
 26// A description of the program, which may be multiline
 27var Description string
 28
 29// A string with the usage of the program. usage: and the name of the program
 30// are automatically prefixed.
 31var Usage string = "[options]"
 32
 33//
 34// Description of options
 35//
 36
 37// The built-in types of options.
 38const (
 39	FLAG = iota
 40	HALF
 41	SINGLE
 42	MULTI
 43)
 44
 45// The built-in types of errors.
 46const (
 47	UNKNOWNERR = iota // unknown option
 48	REQARGERR         // a required argument was not present
 49	NOARGERR          // an argument was present where none should have been
 50)
 51
 52// Whether or not arguments are required
 53const (
 54	NOARG = iota
 55	OPTARG
 56	REQARG
 57)
 58
 59// Parsing is a callback used by Option implementations to report errors.
 60type Parsing struct{}
 61
 62// Error prints the relevant error message and exits.
 63func (Parsing) Error(err int, opt string) {
 64	switch err {
 65	case UNKNOWNERR:
 66		fmt.Fprintf(os.Stderr,
 67			"%s: %s: unknown option\n",
 68			Xname, opt)
 69	case REQARGERR:
 70		fmt.Fprintf(os.Stderr,
 71			"%s: %s: argument required\n",
 72			Xname, opt)
 73	case NOARGERR:
 74		fmt.Fprintf(os.Stderr,
 75			"%s: %s takes no argument\n",
 76			Xname, opt)
 77
 78	}
 79	os.Exit(1)
 80}
 81
 82// Option represents a conceptual option, which there may be multiple methods
 83// of invoking.
 84type Option interface {
 85	// Forms returns a slice with all forms of this option. Forms that
 86	// begin with a single dash are interpreted as short options, multiple
 87	// of which may be combined in one argument (-abcdef). Forms that begin
 88	// with two dashes are interpreted as long options, which must remain
 89	// whole.
 90	Forms() []string
 91	// Description returns the description of this option.
 92	Description() string
 93	// ArgName returns a descriptive name for the argument this option
 94	// takes, or an empty string if this option takes none.
 95	ArgName() string
 96	// Required NOARG, OPTARG, or REQARG
 97	Arg() int
 98	// Invoke is called when this option appears in the command line.
 99	// If the option expects an argument (as indicated by ArgName),
100	// Invoke is guaranteed not to be called without one. Similarly, if
101	// the option does not expect an argument, Invoke is guaranteed to be
102	// called only with the first parameter being the empty string.
103	Invoke(string, Parsing)
104}
105
106// A partial implementation of the Option interface that reflects what most
107// options share.
108type genopt struct {
109	shortform   string
110	longform    string
111	description string
112}
113
114func (o genopt) Forms() []string {
115	forms := make([]string, 0, 2)
116	if len(o.shortform) > 0 {
117		forms = forms[0:1]
118		forms[0] = o.shortform
119	}
120	if len(o.longform) > 0 {
121		forms = forms[0 : len(forms)+1]
122		forms[len(forms)-1] = o.longform
123	}
124	return forms
125}
126
127func (o genopt) Description() string { return o.description }
128
129
130type flag struct {
131	genopt
132	dest *bool
133}
134
135func (flag) ArgName() string { return "" }
136func (o flag) Arg() int      { return NOARG }
137func (o flag) Invoke(string, Parsing) {
138	*o.dest = true
139}
140
141type half struct {
142	genopt
143	dest      *string
144	dflt      string // the value if the option is not given
145	givendflt string // the default value if the option is given
146}
147
148func (o half) ArgName() string { return o.givendflt }
149func (o half) Arg() int        { return OPTARG }
150func (o half) Invoke(arg string, _ Parsing) {
151	if arg == "" {
152		*o.dest = o.givendflt
153	} else {
154		*o.dest = arg
155	}
156}
157
158type single struct {
159	genopt
160	dest *string
161	dflt string
162}
163
164func (o single) ArgName() string { return o.dflt }
165func (o single) Arg() int        { return REQARG }
166func (o single) Invoke(arg string, _ Parsing) {
167	*o.dest = arg
168}
169
170type multi struct {
171	genopt
172	valuedesc string
173	dest      []string
174}
175
176func (o multi) ArgName() string { return o.valuedesc }
177func (o multi) Arg() int        { return REQARG }
178func (o multi) Invoke(arg string, _ Parsing) {
179	o.dest = append(o.dest, arg)
180}
181
182// Stores an option of any kind
183type option struct {
184	dflt         string
185	strdest      *string
186	strslicedest *[]string
187}
188
189// The registered options
190var options map[string]Option = map[string]Option{}
191
192// A plain list of options, for when we need to hit each only once
193var optionList []Option = make([]Option, 0, 1)
194
195// Adds - if there is none.
196func makeShort(s string) string {
197	if len(s) >= 1 && s[0] != '-' {
198		s = "-" + s
199	}
200	return s
201}
202
203// Adds -- if there is none.
204func makeLong(s string) string {
205	s = "--" + strings.TrimLeft(s,"-")
206	return s
207}
208
209// Add adds the given option.
210func Add(opt Option) {
211	for _, form := range opt.Forms() {
212		options[form] = opt
213	}
214	l := len(optionList)
215	if len(optionList)+1 > cap(optionList) {
216		old := optionList
217		optionList = make([]Option, 2*(len(old)+1))
218		copy(optionList, old)
219	}
220	optionList = optionList[0:l+1]
221	optionList[len(optionList)-1]=opt
222}
223
224// Flag creates a new Flag-type option, and adds it, returning the destination.
225func Flag(sform string, lform string, desc string) *bool {
226	dest := new(bool)
227	o := flag{
228		genopt: genopt{
229			shortform:   makeShort(sform),
230			longform:    makeLong(lform),
231			description: desc,
232		},
233		dest: dest,
234	}
235	Add(o)
236	return dest
237}
238
239// ShortFlag is like Flag, but no long form is used.
240func ShortFlag(sform string, desc string) *bool {
241	return Flag(sform, "", desc)
242}
243
244// LongFlag is like Flag, but no short form is used.
245func LongFlag(lform string, desc string) *bool {
246	return Flag("", lform, desc)
247}
248
249// Half creates a new Half-type option, and adds it, returning the destination.
250func Half(sform string, lform string, desc string, dflt string, gdflt string) *string {
251	dest := &dflt
252	o := half{
253		genopt: genopt{
254			shortform:   makeShort(sform),
255			longform:    makeLong(lform),
256			description: desc,
257		},
258		dest:      dest,
259		dflt:      dflt,
260		givendflt: gdflt,
261	}
262	Add(o)
263	return dest
264}
265
266// ShortHalf is like Half, but no long form is used.
267func ShortHalf(sform string, desc string, dflt string, gdflt string) *string {
268	return Half(sform, "", desc, dflt, gdflt)
269}
270
271// LongHalf is like Half, but no short form is used.
272func LongHalf(lform string, desc string, dflt string, gdflt string) *string {
273	return Half("", lform, desc, dflt, gdflt)
274}
275
276// Single creates a new Single-type option, and adds it, returning the destination.
277func Single(sform string, lform string, desc string, dflt string) *string {
278	dest := &dflt
279	o := single{
280		genopt: genopt{
281			shortform:   makeShort(sform),
282			longform:    makeLong(lform),
283			description: desc,
284		},
285		dest: dest,
286		dflt: dflt,
287	}
288	Add(o)
289	return dest
290}
291
292// ShortSingle is like Single, but no long form is used.
293func ShortSingle(sform string, desc string, dflt string) *string {
294	return Single(sform, "", desc, dflt)
295}
296
297// LongSingle is like Single, but no short form is used.
298func LongSingle(lform string, desc string, dflt string) *string {
299	return Single("", lform, desc, dflt)
300}
301
302// Multi creates a new Multi-type option, and adds it, returning the destination.
303func Multi(sform string, lform string, desc string, valuedesc string) []string {
304	o := multi{
305		genopt: genopt{
306			shortform:   makeShort(sform),
307			longform:    makeLong(lform),
308			description: desc,
309		},
310		dest:      make([]string, 0, 4),
311		valuedesc: valuedesc,
312	}
313	Add(o)
314	return o.dest
315}
316
317// ShortMulti is like Multi, but no long form is used.
318func ShortMulti(sform string, desc string, valuedesc string) []string {
319	return Multi(sform, "", desc, valuedesc)
320}
321
322// LongMulti is like Multi, but no short form is used.
323func LongMulti(lform string, desc string, valuedesc string) []string {
324	return Multi("", lform, desc, valuedesc)
325}
326
327// True if the option list has been terminated by '-', false otherwise.
328var optsOver bool
329
330// Parse performs parsing of the command line, making complete information
331// available to the program.
332func Parse() {
333	ParseArgs(os.Args)
334}
335
336// ParseArgs performs parsing of the given command line, making complete
337// information available to the program.
338//
339// This function was created specifically to enable unit testing - the proper
340// entry point for most programs is Parse.
341func ParseArgs(args []string) {
342	addHelp()
343	p := Parsing{}
344	for i := 1; i < len(args); i++ {
345		arg := args[i]
346		if len(arg) > 0 && arg[0] == '-' && !optsOver {
347			switch {
348			case len(arg) == 1:
349				// blank option - end option parsing
350				optsOver = true
351			case arg[1] == '-':
352				// long option
353				argparts := strings.SplitN(arg, "=", 2)
354				var val string
355				if len(argparts) == 2 {
356					arg, val = argparts[0], argparts[1]
357				}
358				if option, ok := options[arg]; ok {
359					switch {
360					case val == "" && option.Arg() != REQARG:
361						option.Invoke(val, p)
362					case val != "" && option.Arg() != NOARG:
363						option.Invoke(val, p)
364					case val == "" && option.Arg() == REQARG:
365						p.Error(REQARGERR, arg)
366					case val != "" && option.Arg() == NOARG:
367						p.Error(NOARGERR, arg)
368					}
369				} else {
370					p.Error(UNKNOWNERR, arg)
371				}
372			default:
373				// short option series
374				for j, optChar := range arg[1:len(arg)] {
375					opt := string(optChar)
376					if option, ok := options["-"+opt]; ok {
377						if option.ArgName() == "" {
378							option.Invoke("", p)
379							continue
380						}
381						// handle both -Oarg and -O arg
382						if j != len(arg)-2 {
383							val := arg[j+2 : len(arg)]
384							option.Invoke(val, p)
385							break
386						}
387						i++
388						if i < len(args) {
389							option.Invoke(args[i], p)
390						} else if option.Arg() == REQARG {
391							p.Error(REQARGERR, arg)
392						} else {
393							option.Invoke("", p)
394						}
395					} else {
396						p.Error(UNKNOWNERR, "-"+opt)
397					}
398				}
399			}
400		} else {
401			Args = Args[0 : len(Args)+1]
402			Args[len(Args)-1] = arg
403		}
404	}
405	if *printHelp {
406		Help()
407		os.Exit(0)
408	}
409}