EXPOSE 9115
ENTRYPOINT [ "/bin/blackbox_exporter" ]
-CMD [ "-config.file=/etc/blackbox_exporter/config.yml" ]
+CMD [ "--config.file=/etc/blackbox_exporter/config.yml" ]
### Building with Docker
docker build -t blackbox_exporter .
- docker run -d -p 9115:9115 --name blackbox_exporter -v `pwd`:/config blackbox_exporter -config.file=/config/blackbox.yml
+ docker run -d -p 9115:9115 --name blackbox_exporter -v `pwd`:/config blackbox_exporter --config.file=/config/blackbox.yml
## Configuration
To view all available command-line flags, run `./blackbox_exporter -h`.
-To specify which configuration file to load, use the `-config.file` flag.
+To specify which configuration file to load, use the `--config.file` flag.
The file is written in [YAML format](https://en.wikipedia.org/wiki/YAML), defined by the scheme described which can be found [here.](https://github.com/prometheus/blackbox_exporter/blob/master/CONFIGURATION.md)
Additionally, an [example configuration](https://github.com/prometheus/blackbox_exporter/blob/master/example.yml) is also available.
import (
"context"
- "flag"
"fmt"
"io/ioutil"
"net/http"
"syscall"
"time"
+ "gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/yaml.v2"
"github.com/prometheus/client_golang/prometheus"
sc = &SafeConfig{
C: &Config{},
}
- configFile = flag.String("config.file", "blackbox.yml", "Blackbox exporter configuration file.")
- listenAddress = flag.String("web.listen-address", ":9115", "The address to listen on for HTTP requests.")
- showVersion = flag.Bool("version", false, "Print version information.")
- timeoutOffset = flag.Float64("timeout-offset", 0.5, "Offset to subtract from timeout in seconds.")
+
+ configFile = kingpin.Flag("config.flag", "Blackbox exporter configuration file.").Default("blackbox.yml").String()
+ listenAddress = kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests.").Default(":9115").String()
+ timeoutOffset = kingpin.Flag("timeout-offset", "Offset to subtract from timeout in seconds.").Default("0.5").Float64()
)
var Probers = map[string]func(context.Context, string, Module, *prometheus.Registry) bool{
}
func main() {
- flag.Parse()
-
- if *showVersion {
- fmt.Fprintln(os.Stdout, version.Print("blackbox_exporter"))
- os.Exit(0)
- }
+ kingpin.Version(version.Print("blackbox_exporter"))
+ kingpin.HelpFlag.Short('h')
+ kingpin.Parse()
log.Infoln("Starting blackbox_exporter", version.Info())
log.Infoln("Build context", version.BuildContext())
--- /dev/null
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+# Go's `text/template` package with newline elision
+
+This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline.
+
+eg.
+
+```
+{{if true}}\
+hello
+{{end}}\
+```
+
+Will result in:
+
+```
+hello\n
+```
+
+Rather than:
+
+```
+\n
+hello\n
+\n
+```
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package template implements data-driven templates for generating textual output.
+
+To generate HTML output, see package html/template, which has the same interface
+as this package but automatically secures HTML output against certain attacks.
+
+Templates are executed by applying them to a data structure. Annotations in the
+template refer to elements of the data structure (typically a field of a struct
+or a key in a map) to control execution and derive values to be displayed.
+Execution of the template walks the structure and sets the cursor, represented
+by a period '.' and called "dot", to the value at the current location in the
+structure as execution proceeds.
+
+The input text for a template is UTF-8-encoded text in any format.
+"Actions"--data evaluations or control structures--are delimited by
+"{{" and "}}"; all text outside actions is copied to the output unchanged.
+Actions may not span newlines, although comments can.
+
+Once parsed, a template may be executed safely in parallel.
+
+Here is a trivial example that prints "17 items are made of wool".
+
+ type Inventory struct {
+ Material string
+ Count uint
+ }
+ sweaters := Inventory{"wool", 17}
+ tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
+ if err != nil { panic(err) }
+ err = tmpl.Execute(os.Stdout, sweaters)
+ if err != nil { panic(err) }
+
+More intricate examples appear below.
+
+Actions
+
+Here is the list of actions. "Arguments" and "pipelines" are evaluations of
+data, defined in detail below.
+
+*/
+// {{/* a comment */}}
+// A comment; discarded. May contain newlines.
+// Comments do not nest and must start and end at the
+// delimiters, as shown here.
+/*
+
+ {{pipeline}}
+ The default textual representation of the value of the pipeline
+ is copied to the output.
+
+ {{if pipeline}} T1 {{end}}
+ If the value of the pipeline is empty, no output is generated;
+ otherwise, T1 is executed. The empty values are false, 0, any
+ nil pointer or interface value, and any array, slice, map, or
+ string of length zero.
+ Dot is unaffected.
+
+ {{if pipeline}} T1 {{else}} T0 {{end}}
+ If the value of the pipeline is empty, T0 is executed;
+ otherwise, T1 is executed. Dot is unaffected.
+
+ {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
+ To simplify the appearance of if-else chains, the else action
+ of an if may include another if directly; the effect is exactly
+ the same as writing
+ {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
+
+ {{range pipeline}} T1 {{end}}
+ The value of the pipeline must be an array, slice, map, or channel.
+ If the value of the pipeline has length zero, nothing is output;
+ otherwise, dot is set to the successive elements of the array,
+ slice, or map and T1 is executed. If the value is a map and the
+ keys are of basic type with a defined order ("comparable"), the
+ elements will be visited in sorted key order.
+
+ {{range pipeline}} T1 {{else}} T0 {{end}}
+ The value of the pipeline must be an array, slice, map, or channel.
+ If the value of the pipeline has length zero, dot is unaffected and
+ T0 is executed; otherwise, dot is set to the successive elements
+ of the array, slice, or map and T1 is executed.
+
+ {{template "name"}}
+ The template with the specified name is executed with nil data.
+
+ {{template "name" pipeline}}
+ The template with the specified name is executed with dot set
+ to the value of the pipeline.
+
+ {{with pipeline}} T1 {{end}}
+ If the value of the pipeline is empty, no output is generated;
+ otherwise, dot is set to the value of the pipeline and T1 is
+ executed.
+
+ {{with pipeline}} T1 {{else}} T0 {{end}}
+ If the value of the pipeline is empty, dot is unaffected and T0
+ is executed; otherwise, dot is set to the value of the pipeline
+ and T1 is executed.
+
+Arguments
+
+An argument is a simple value, denoted by one of the following.
+
+ - A boolean, string, character, integer, floating-point, imaginary
+ or complex constant in Go syntax. These behave like Go's untyped
+ constants, although raw strings may not span newlines.
+ - The keyword nil, representing an untyped Go nil.
+ - The character '.' (period):
+ .
+ The result is the value of dot.
+ - A variable name, which is a (possibly empty) alphanumeric string
+ preceded by a dollar sign, such as
+ $piOver2
+ or
+ $
+ The result is the value of the variable.
+ Variables are described below.
+ - The name of a field of the data, which must be a struct, preceded
+ by a period, such as
+ .Field
+ The result is the value of the field. Field invocations may be
+ chained:
+ .Field1.Field2
+ Fields can also be evaluated on variables, including chaining:
+ $x.Field1.Field2
+ - The name of a key of the data, which must be a map, preceded
+ by a period, such as
+ .Key
+ The result is the map element value indexed by the key.
+ Key invocations may be chained and combined with fields to any
+ depth:
+ .Field1.Key1.Field2.Key2
+ Although the key must be an alphanumeric identifier, unlike with
+ field names they do not need to start with an upper case letter.
+ Keys can also be evaluated on variables, including chaining:
+ $x.key1.key2
+ - The name of a niladic method of the data, preceded by a period,
+ such as
+ .Method
+ The result is the value of invoking the method with dot as the
+ receiver, dot.Method(). Such a method must have one return value (of
+ any type) or two return values, the second of which is an error.
+ If it has two and the returned error is non-nil, execution terminates
+ and an error is returned to the caller as the value of Execute.
+ Method invocations may be chained and combined with fields and keys
+ to any depth:
+ .Field1.Key1.Method1.Field2.Key2.Method2
+ Methods can also be evaluated on variables, including chaining:
+ $x.Method1.Field
+ - The name of a niladic function, such as
+ fun
+ The result is the value of invoking the function, fun(). The return
+ types and values behave as in methods. Functions and function
+ names are described below.
+ - A parenthesized instance of one the above, for grouping. The result
+ may be accessed by a field or map key invocation.
+ print (.F1 arg1) (.F2 arg2)
+ (.StructValuedMethod "arg").Field
+
+Arguments may evaluate to any type; if they are pointers the implementation
+automatically indirects to the base type when required.
+If an evaluation yields a function value, such as a function-valued
+field of a struct, the function is not invoked automatically, but it
+can be used as a truth value for an if action and the like. To invoke
+it, use the call function, defined below.
+
+A pipeline is a possibly chained sequence of "commands". A command is a simple
+value (argument) or a function or method call, possibly with multiple arguments:
+
+ Argument
+ The result is the value of evaluating the argument.
+ .Method [Argument...]
+ The method can be alone or the last element of a chain but,
+ unlike methods in the middle of a chain, it can take arguments.
+ The result is the value of calling the method with the
+ arguments:
+ dot.Method(Argument1, etc.)
+ functionName [Argument...]
+ The result is the value of calling the function associated
+ with the name:
+ function(Argument1, etc.)
+ Functions and function names are described below.
+
+Pipelines
+
+A pipeline may be "chained" by separating a sequence of commands with pipeline
+characters '|'. In a chained pipeline, the result of the each command is
+passed as the last argument of the following command. The output of the final
+command in the pipeline is the value of the pipeline.
+
+The output of a command will be either one value or two values, the second of
+which has type error. If that second value is present and evaluates to
+non-nil, execution terminates and the error is returned to the caller of
+Execute.
+
+Variables
+
+A pipeline inside an action may initialize a variable to capture the result.
+The initialization has syntax
+
+ $variable := pipeline
+
+where $variable is the name of the variable. An action that declares a
+variable produces no output.
+
+If a "range" action initializes a variable, the variable is set to the
+successive elements of the iteration. Also, a "range" may declare two
+variables, separated by a comma:
+
+ range $index, $element := pipeline
+
+in which case $index and $element are set to the successive values of the
+array/slice index or map key and element, respectively. Note that if there is
+only one variable, it is assigned the element; this is opposite to the
+convention in Go range clauses.
+
+A variable's scope extends to the "end" action of the control structure ("if",
+"with", or "range") in which it is declared, or to the end of the template if
+there is no such control structure. A template invocation does not inherit
+variables from the point of its invocation.
+
+When execution begins, $ is set to the data argument passed to Execute, that is,
+to the starting value of dot.
+
+Examples
+
+Here are some example one-line templates demonstrating pipelines and variables.
+All produce the quoted word "output":
+
+ {{"\"output\""}}
+ A string constant.
+ {{`"output"`}}
+ A raw string constant.
+ {{printf "%q" "output"}}
+ A function call.
+ {{"output" | printf "%q"}}
+ A function call whose final argument comes from the previous
+ command.
+ {{printf "%q" (print "out" "put")}}
+ A parenthesized argument.
+ {{"put" | printf "%s%s" "out" | printf "%q"}}
+ A more elaborate call.
+ {{"output" | printf "%s" | printf "%q"}}
+ A longer chain.
+ {{with "output"}}{{printf "%q" .}}{{end}}
+ A with action using dot.
+ {{with $x := "output" | printf "%q"}}{{$x}}{{end}}
+ A with action that creates and uses a variable.
+ {{with $x := "output"}}{{printf "%q" $x}}{{end}}
+ A with action that uses the variable in another action.
+ {{with $x := "output"}}{{$x | printf "%q"}}{{end}}
+ The same, but pipelined.
+
+Functions
+
+During execution functions are found in two function maps: first in the
+template, then in the global function map. By default, no functions are defined
+in the template but the Funcs method can be used to add them.
+
+Predefined global functions are named as follows.
+
+ and
+ Returns the boolean AND of its arguments by returning the
+ first empty argument or the last argument, that is,
+ "and x y" behaves as "if x then y else x". All the
+ arguments are evaluated.
+ call
+ Returns the result of calling the first argument, which
+ must be a function, with the remaining arguments as parameters.
+ Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
+ Y is a func-valued field, map entry, or the like.
+ The first argument must be the result of an evaluation
+ that yields a value of function type (as distinct from
+ a predefined function such as print). The function must
+ return either one or two result values, the second of which
+ is of type error. If the arguments don't match the function
+ or the returned error value is non-nil, execution stops.
+ html
+ Returns the escaped HTML equivalent of the textual
+ representation of its arguments.
+ index
+ Returns the result of indexing its first argument by the
+ following arguments. Thus "index x 1 2 3" is, in Go syntax,
+ x[1][2][3]. Each indexed item must be a map, slice, or array.
+ js
+ Returns the escaped JavaScript equivalent of the textual
+ representation of its arguments.
+ len
+ Returns the integer length of its argument.
+ not
+ Returns the boolean negation of its single argument.
+ or
+ Returns the boolean OR of its arguments by returning the
+ first non-empty argument or the last argument, that is,
+ "or x y" behaves as "if x then x else y". All the
+ arguments are evaluated.
+ print
+ An alias for fmt.Sprint
+ printf
+ An alias for fmt.Sprintf
+ println
+ An alias for fmt.Sprintln
+ urlquery
+ Returns the escaped value of the textual representation of
+ its arguments in a form suitable for embedding in a URL query.
+
+The boolean functions take any zero value to be false and a non-zero
+value to be true.
+
+There is also a set of binary comparison operators defined as
+functions:
+
+ eq
+ Returns the boolean truth of arg1 == arg2
+ ne
+ Returns the boolean truth of arg1 != arg2
+ lt
+ Returns the boolean truth of arg1 < arg2
+ le
+ Returns the boolean truth of arg1 <= arg2
+ gt
+ Returns the boolean truth of arg1 > arg2
+ ge
+ Returns the boolean truth of arg1 >= arg2
+
+For simpler multi-way equality tests, eq (only) accepts two or more
+arguments and compares the second and subsequent to the first,
+returning in effect
+
+ arg1==arg2 || arg1==arg3 || arg1==arg4 ...
+
+(Unlike with || in Go, however, eq is a function call and all the
+arguments will be evaluated.)
+
+The comparison functions work on basic types only (or named basic
+types, such as "type Celsius float32"). They implement the Go rules
+for comparison of values, except that size and exact type are
+ignored, so any integer value, signed or unsigned, may be compared
+with any other integer value. (The arithmetic value is compared,
+not the bit pattern, so all negative integers are less than all
+unsigned integers.) However, as usual, one may not compare an int
+with a float32 and so on.
+
+Associated templates
+
+Each template is named by a string specified when it is created. Also, each
+template is associated with zero or more other templates that it may invoke by
+name; such associations are transitive and form a name space of templates.
+
+A template may use a template invocation to instantiate another associated
+template; see the explanation of the "template" action above. The name must be
+that of a template associated with the template that contains the invocation.
+
+Nested template definitions
+
+When parsing a template, another template may be defined and associated with the
+template being parsed. Template definitions must appear at the top level of the
+template, much like global variables in a Go program.
+
+The syntax of such definitions is to surround each template declaration with a
+"define" and "end" action.
+
+The define action names the template being created by providing a string
+constant. Here is a simple example:
+
+ `{{define "T1"}}ONE{{end}}
+ {{define "T2"}}TWO{{end}}
+ {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
+ {{template "T3"}}`
+
+This defines two templates, T1 and T2, and a third T3 that invokes the other two
+when it is executed. Finally it invokes T3. If executed this template will
+produce the text
+
+ ONE TWO
+
+By construction, a template may reside in only one association. If it's
+necessary to have a template addressable from multiple associations, the
+template definition must be parsed multiple times to create distinct *Template
+values, or must be copied with the Clone or AddParseTree method.
+
+Parse may be called multiple times to assemble the various associated templates;
+see the ParseFiles and ParseGlob functions and methods for simple ways to parse
+related templates stored in files.
+
+A template may be executed directly or through ExecuteTemplate, which executes
+an associated template identified by name. To invoke our example above, we
+might write,
+
+ err := tmpl.Execute(os.Stdout, "no data needed")
+ if err != nil {
+ log.Fatalf("execution failed: %s", err)
+ }
+
+or to invoke a particular template explicitly by name,
+
+ err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
+ if err != nil {
+ log.Fatalf("execution failed: %s", err)
+ }
+
+*/
+package template
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+
+ "github.com/alecthomas/template/parse"
+)
+
+// state represents the state of an execution. It's not part of the
+// template so that multiple executions of the same template
+// can execute in parallel.
+type state struct {
+ tmpl *Template
+ wr io.Writer
+ node parse.Node // current node, for errors
+ vars []variable // push-down stack of variable values.
+}
+
+// variable holds the dynamic value of a variable such as $, $x etc.
+type variable struct {
+ name string
+ value reflect.Value
+}
+
+// push pushes a new variable on the stack.
+func (s *state) push(name string, value reflect.Value) {
+ s.vars = append(s.vars, variable{name, value})
+}
+
+// mark returns the length of the variable stack.
+func (s *state) mark() int {
+ return len(s.vars)
+}
+
+// pop pops the variable stack up to the mark.
+func (s *state) pop(mark int) {
+ s.vars = s.vars[0:mark]
+}
+
+// setVar overwrites the top-nth variable on the stack. Used by range iterations.
+func (s *state) setVar(n int, value reflect.Value) {
+ s.vars[len(s.vars)-n].value = value
+}
+
+// varValue returns the value of the named variable.
+func (s *state) varValue(name string) reflect.Value {
+ for i := s.mark() - 1; i >= 0; i-- {
+ if s.vars[i].name == name {
+ return s.vars[i].value
+ }
+ }
+ s.errorf("undefined variable: %s", name)
+ return zero
+}
+
+var zero reflect.Value
+
+// at marks the state to be on node n, for error reporting.
+func (s *state) at(node parse.Node) {
+ s.node = node
+}
+
+// doublePercent returns the string with %'s replaced by %%, if necessary,
+// so it can be used safely inside a Printf format string.
+func doublePercent(str string) string {
+ if strings.Contains(str, "%") {
+ str = strings.Replace(str, "%", "%%", -1)
+ }
+ return str
+}
+
+// errorf formats the error and terminates processing.
+func (s *state) errorf(format string, args ...interface{}) {
+ name := doublePercent(s.tmpl.Name())
+ if s.node == nil {
+ format = fmt.Sprintf("template: %s: %s", name, format)
+ } else {
+ location, context := s.tmpl.ErrorContext(s.node)
+ format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)
+ }
+ panic(fmt.Errorf(format, args...))
+}
+
+// errRecover is the handler that turns panics into returns from the top
+// level of Parse.
+func errRecover(errp *error) {
+ e := recover()
+ if e != nil {
+ switch err := e.(type) {
+ case runtime.Error:
+ panic(e)
+ case error:
+ *errp = err
+ default:
+ panic(e)
+ }
+ }
+}
+
+// ExecuteTemplate applies the template associated with t that has the given name
+// to the specified data object and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
+func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
+ tmpl := t.tmpl[name]
+ if tmpl == nil {
+ return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
+ }
+ return tmpl.Execute(wr, data)
+}
+
+// Execute applies a parsed template to the specified data object,
+// and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
+func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
+ defer errRecover(&err)
+ value := reflect.ValueOf(data)
+ state := &state{
+ tmpl: t,
+ wr: wr,
+ vars: []variable{{"$", value}},
+ }
+ t.init()
+ if t.Tree == nil || t.Root == nil {
+ var b bytes.Buffer
+ for name, tmpl := range t.tmpl {
+ if tmpl.Tree == nil || tmpl.Root == nil {
+ continue
+ }
+ if b.Len() > 0 {
+ b.WriteString(", ")
+ }
+ fmt.Fprintf(&b, "%q", name)
+ }
+ var s string
+ if b.Len() > 0 {
+ s = "; defined templates are: " + b.String()
+ }
+ state.errorf("%q is an incomplete or empty template%s", t.Name(), s)
+ }
+ state.walk(value, t.Root)
+ return
+}
+
+// Walk functions step through the major pieces of the template structure,
+// generating output as they go.
+func (s *state) walk(dot reflect.Value, node parse.Node) {
+ s.at(node)
+ switch node := node.(type) {
+ case *parse.ActionNode:
+ // Do not pop variables so they persist until next end.
+ // Also, if the action declares variables, don't print the result.
+ val := s.evalPipeline(dot, node.Pipe)
+ if len(node.Pipe.Decl) == 0 {
+ s.printValue(node, val)
+ }
+ case *parse.IfNode:
+ s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
+ case *parse.ListNode:
+ for _, node := range node.Nodes {
+ s.walk(dot, node)
+ }
+ case *parse.RangeNode:
+ s.walkRange(dot, node)
+ case *parse.TemplateNode:
+ s.walkTemplate(dot, node)
+ case *parse.TextNode:
+ if _, err := s.wr.Write(node.Text); err != nil {
+ s.errorf("%s", err)
+ }
+ case *parse.WithNode:
+ s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)
+ default:
+ s.errorf("unknown node: %s", node)
+ }
+}
+
+// walkIfOrWith walks an 'if' or 'with' node. The two control structures
+// are identical in behavior except that 'with' sets dot.
+func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {
+ defer s.pop(s.mark())
+ val := s.evalPipeline(dot, pipe)
+ truth, ok := isTrue(val)
+ if !ok {
+ s.errorf("if/with can't use %v", val)
+ }
+ if truth {
+ if typ == parse.NodeWith {
+ s.walk(val, list)
+ } else {
+ s.walk(dot, list)
+ }
+ } else if elseList != nil {
+ s.walk(dot, elseList)
+ }
+}
+
+// isTrue reports whether the value is 'true', in the sense of not the zero of its type,
+// and whether the value has a meaningful truth value.
+func isTrue(val reflect.Value) (truth, ok bool) {
+ if !val.IsValid() {
+ // Something like var x interface{}, never set. It's a form of nil.
+ return false, true
+ }
+ switch val.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ truth = val.Len() > 0
+ case reflect.Bool:
+ truth = val.Bool()
+ case reflect.Complex64, reflect.Complex128:
+ truth = val.Complex() != 0
+ case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
+ truth = !val.IsNil()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ truth = val.Int() != 0
+ case reflect.Float32, reflect.Float64:
+ truth = val.Float() != 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ truth = val.Uint() != 0
+ case reflect.Struct:
+ truth = true // Struct values are always true.
+ default:
+ return
+ }
+ return truth, true
+}
+
+func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
+ s.at(r)
+ defer s.pop(s.mark())
+ val, _ := indirect(s.evalPipeline(dot, r.Pipe))
+ // mark top of stack before any variables in the body are pushed.
+ mark := s.mark()
+ oneIteration := func(index, elem reflect.Value) {
+ // Set top var (lexically the second if there are two) to the element.
+ if len(r.Pipe.Decl) > 0 {
+ s.setVar(1, elem)
+ }
+ // Set next var (lexically the first if there are two) to the index.
+ if len(r.Pipe.Decl) > 1 {
+ s.setVar(2, index)
+ }
+ s.walk(elem, r.List)
+ s.pop(mark)
+ }
+ switch val.Kind() {
+ case reflect.Array, reflect.Slice:
+ if val.Len() == 0 {
+ break
+ }
+ for i := 0; i < val.Len(); i++ {
+ oneIteration(reflect.ValueOf(i), val.Index(i))
+ }
+ return
+ case reflect.Map:
+ if val.Len() == 0 {
+ break
+ }
+ for _, key := range sortKeys(val.MapKeys()) {
+ oneIteration(key, val.MapIndex(key))
+ }
+ return
+ case reflect.Chan:
+ if val.IsNil() {
+ break
+ }
+ i := 0
+ for ; ; i++ {
+ elem, ok := val.Recv()
+ if !ok {
+ break
+ }
+ oneIteration(reflect.ValueOf(i), elem)
+ }
+ if i == 0 {
+ break
+ }
+ return
+ case reflect.Invalid:
+ break // An invalid value is likely a nil map, etc. and acts like an empty map.
+ default:
+ s.errorf("range can't iterate over %v", val)
+ }
+ if r.ElseList != nil {
+ s.walk(dot, r.ElseList)
+ }
+}
+
+func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
+ s.at(t)
+ tmpl := s.tmpl.tmpl[t.Name]
+ if tmpl == nil {
+ s.errorf("template %q not defined", t.Name)
+ }
+ // Variables declared by the pipeline persist.
+ dot = s.evalPipeline(dot, t.Pipe)
+ newState := *s
+ newState.tmpl = tmpl
+ // No dynamic scoping: template invocations inherit no variables.
+ newState.vars = []variable{{"$", dot}}
+ newState.walk(dot, tmpl.Root)
+}
+
+// Eval functions evaluate pipelines, commands, and their elements and extract
+// values from the data structure by examining fields, calling methods, and so on.
+// The printing of those values happens only through walk functions.
+
+// evalPipeline returns the value acquired by evaluating a pipeline. If the
+// pipeline has a variable declaration, the variable will be pushed on the
+// stack. Callers should therefore pop the stack after they are finished
+// executing commands depending on the pipeline value.
+func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) {
+ if pipe == nil {
+ return
+ }
+ s.at(pipe)
+ for _, cmd := range pipe.Cmds {
+ value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
+ // If the object has type interface{}, dig down one level to the thing inside.
+ if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
+ value = reflect.ValueOf(value.Interface()) // lovely!
+ }
+ }
+ for _, variable := range pipe.Decl {
+ s.push(variable.Ident[0], value)
+ }
+ return value
+}
+
+func (s *state) notAFunction(args []parse.Node, final reflect.Value) {
+ if len(args) > 1 || final.IsValid() {
+ s.errorf("can't give argument to non-function %s", args[0])
+ }
+}
+
+func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value {
+ firstWord := cmd.Args[0]
+ switch n := firstWord.(type) {
+ case *parse.FieldNode:
+ return s.evalFieldNode(dot, n, cmd.Args, final)
+ case *parse.ChainNode:
+ return s.evalChainNode(dot, n, cmd.Args, final)
+ case *parse.IdentifierNode:
+ // Must be a function.
+ return s.evalFunction(dot, n, cmd, cmd.Args, final)
+ case *parse.PipeNode:
+ // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored.
+ return s.evalPipeline(dot, n)
+ case *parse.VariableNode:
+ return s.evalVariableNode(dot, n, cmd.Args, final)
+ }
+ s.at(firstWord)
+ s.notAFunction(cmd.Args, final)
+ switch word := firstWord.(type) {
+ case *parse.BoolNode:
+ return reflect.ValueOf(word.True)
+ case *parse.DotNode:
+ return dot
+ case *parse.NilNode:
+ s.errorf("nil is not a command")
+ case *parse.NumberNode:
+ return s.idealConstant(word)
+ case *parse.StringNode:
+ return reflect.ValueOf(word.Text)
+ }
+ s.errorf("can't evaluate command %q", firstWord)
+ panic("not reached")
+}
+
+// idealConstant is called to return the value of a number in a context where
+// we don't know the type. In that case, the syntax of the number tells us
+// its type, and we use Go rules to resolve. Note there is no such thing as
+// a uint ideal constant in this situation - the value must be of int type.
+func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
+ // These are ideal constants but we don't know the type
+ // and we have no context. (If it was a method argument,
+ // we'd know what we need.) The syntax guides us to some extent.
+ s.at(constant)
+ switch {
+ case constant.IsComplex:
+ return reflect.ValueOf(constant.Complex128) // incontrovertible.
+ case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0:
+ return reflect.ValueOf(constant.Float64)
+ case constant.IsInt:
+ n := int(constant.Int64)
+ if int64(n) != constant.Int64 {
+ s.errorf("%s overflows int", constant.Text)
+ }
+ return reflect.ValueOf(n)
+ case constant.IsUint:
+ s.errorf("%s overflows int", constant.Text)
+ }
+ return zero
+}
+
+func isHexConstant(s string) bool {
+ return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
+}
+
+func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {
+ s.at(field)
+ return s.evalFieldChain(dot, dot, field, field.Ident, args, final)
+}
+
+func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {
+ s.at(chain)
+ // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.
+ pipe := s.evalArg(dot, nil, chain.Node)
+ if len(chain.Field) == 0 {
+ s.errorf("internal error: no fields in evalChainNode")
+ }
+ return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)
+}
+
+func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {
+ // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.
+ s.at(variable)
+ value := s.varValue(variable.Ident[0])
+ if len(variable.Ident) == 1 {
+ s.notAFunction(args, final)
+ return value
+ }
+ return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)
+}
+
+// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.
+// dot is the environment in which to evaluate arguments, while
+// receiver is the value being walked along the chain.
+func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {
+ n := len(ident)
+ for i := 0; i < n-1; i++ {
+ receiver = s.evalField(dot, ident[i], node, nil, zero, receiver)
+ }
+ // Now if it's a method, it gets the arguments.
+ return s.evalField(dot, ident[n-1], node, args, final, receiver)
+}
+
+func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
+ s.at(node)
+ name := node.Ident
+ function, ok := findFunction(name, s.tmpl)
+ if !ok {
+ s.errorf("%q is not a defined function", name)
+ }
+ return s.evalCall(dot, function, cmd, name, args, final)
+}
+
+// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
+// The 'final' argument represents the return value from the preceding
+// value of the pipeline, if any.
+func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
+ if !receiver.IsValid() {
+ return zero
+ }
+ typ := receiver.Type()
+ receiver, _ = indirect(receiver)
+ // Unless it's an interface, need to get to a value of type *T to guarantee
+ // we see all methods of T and *T.
+ ptr := receiver
+ if ptr.Kind() != reflect.Interface && ptr.CanAddr() {
+ ptr = ptr.Addr()
+ }
+ if method := ptr.MethodByName(fieldName); method.IsValid() {
+ return s.evalCall(dot, method, node, fieldName, args, final)
+ }
+ hasArgs := len(args) > 1 || final.IsValid()
+ // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil.
+ receiver, isNil := indirect(receiver)
+ if isNil {
+ s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
+ }
+ switch receiver.Kind() {
+ case reflect.Struct:
+ tField, ok := receiver.Type().FieldByName(fieldName)
+ if ok {
+ field := receiver.FieldByIndex(tField.Index)
+ if tField.PkgPath != "" { // field is unexported
+ s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
+ }
+ // If it's a function, we must call it.
+ if hasArgs {
+ s.errorf("%s has arguments but cannot be invoked as function", fieldName)
+ }
+ return field
+ }
+ s.errorf("%s is not a field of struct type %s", fieldName, typ)
+ case reflect.Map:
+ // If it's a map, attempt to use the field name as a key.
+ nameVal := reflect.ValueOf(fieldName)
+ if nameVal.Type().AssignableTo(receiver.Type().Key()) {
+ if hasArgs {
+ s.errorf("%s is not a method but has arguments", fieldName)
+ }
+ return receiver.MapIndex(nameVal)
+ }
+ }
+ s.errorf("can't evaluate field %s in type %s", fieldName, typ)
+ panic("not reached")
+}
+
+var (
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
+ fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+)
+
+// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
+// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
+// as the function itself.
+func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {
+ if args != nil {
+ args = args[1:] // Zeroth arg is function name/node; not passed to function.
+ }
+ typ := fun.Type()
+ numIn := len(args)
+ if final.IsValid() {
+ numIn++
+ }
+ numFixed := len(args)
+ if typ.IsVariadic() {
+ numFixed = typ.NumIn() - 1 // last arg is the variadic one.
+ if numIn < numFixed {
+ s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
+ }
+ } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() {
+ s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args))
+ }
+ if !goodFunc(typ) {
+ // TODO: This could still be a confusing error; maybe goodFunc should provide info.
+ s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
+ }
+ // Build the arg list.
+ argv := make([]reflect.Value, numIn)
+ // Args must be evaluated. Fixed args first.
+ i := 0
+ for ; i < numFixed && i < len(args); i++ {
+ argv[i] = s.evalArg(dot, typ.In(i), args[i])
+ }
+ // Now the ... args.
+ if typ.IsVariadic() {
+ argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
+ for ; i < len(args); i++ {
+ argv[i] = s.evalArg(dot, argType, args[i])
+ }
+ }
+ // Add final value if necessary.
+ if final.IsValid() {
+ t := typ.In(typ.NumIn() - 1)
+ if typ.IsVariadic() {
+ t = t.Elem()
+ }
+ argv[i] = s.validateType(final, t)
+ }
+ result := fun.Call(argv)
+ // If we have an error that is not nil, stop execution and return that error to the caller.
+ if len(result) == 2 && !result[1].IsNil() {
+ s.at(node)
+ s.errorf("error calling %s: %s", name, result[1].Interface().(error))
+ }
+ return result[0]
+}
+
+// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
+func canBeNil(typ reflect.Type) bool {
+ switch typ.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return true
+ }
+ return false
+}
+
+// validateType guarantees that the value is valid and assignable to the type.
+func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {
+ if !value.IsValid() {
+ if typ == nil || canBeNil(typ) {
+ // An untyped nil interface{}. Accept as a proper nil value.
+ return reflect.Zero(typ)
+ }
+ s.errorf("invalid value; expected %s", typ)
+ }
+ if typ != nil && !value.Type().AssignableTo(typ) {
+ if value.Kind() == reflect.Interface && !value.IsNil() {
+ value = value.Elem()
+ if value.Type().AssignableTo(typ) {
+ return value
+ }
+ // fallthrough
+ }
+ // Does one dereference or indirection work? We could do more, as we
+ // do with method receivers, but that gets messy and method receivers
+ // are much more constrained, so it makes more sense there than here.
+ // Besides, one is almost always all you need.
+ switch {
+ case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ):
+ value = value.Elem()
+ if !value.IsValid() {
+ s.errorf("dereference of nil pointer of type %s", typ)
+ }
+ case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
+ value = value.Addr()
+ default:
+ s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
+ }
+ }
+ return value
+}
+
+func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ switch arg := n.(type) {
+ case *parse.DotNode:
+ return s.validateType(dot, typ)
+ case *parse.NilNode:
+ if canBeNil(typ) {
+ return reflect.Zero(typ)
+ }
+ s.errorf("cannot assign nil to %s", typ)
+ case *parse.FieldNode:
+ return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ)
+ case *parse.VariableNode:
+ return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ)
+ case *parse.PipeNode:
+ return s.validateType(s.evalPipeline(dot, arg), typ)
+ case *parse.IdentifierNode:
+ return s.evalFunction(dot, arg, arg, nil, zero)
+ case *parse.ChainNode:
+ return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
+ }
+ switch typ.Kind() {
+ case reflect.Bool:
+ return s.evalBool(typ, n)
+ case reflect.Complex64, reflect.Complex128:
+ return s.evalComplex(typ, n)
+ case reflect.Float32, reflect.Float64:
+ return s.evalFloat(typ, n)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return s.evalInteger(typ, n)
+ case reflect.Interface:
+ if typ.NumMethod() == 0 {
+ return s.evalEmptyInterface(dot, n)
+ }
+ case reflect.String:
+ return s.evalString(typ, n)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return s.evalUnsignedInteger(typ, n)
+ }
+ s.errorf("can't handle %s for arg of type %s", n, typ)
+ panic("not reached")
+}
+
+func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ if n, ok := n.(*parse.BoolNode); ok {
+ value := reflect.New(typ).Elem()
+ value.SetBool(n.True)
+ return value
+ }
+ s.errorf("expected bool; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ if n, ok := n.(*parse.StringNode); ok {
+ value := reflect.New(typ).Elem()
+ value.SetString(n.Text)
+ return value
+ }
+ s.errorf("expected string; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ if n, ok := n.(*parse.NumberNode); ok && n.IsInt {
+ value := reflect.New(typ).Elem()
+ value.SetInt(n.Int64)
+ return value
+ }
+ s.errorf("expected integer; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ if n, ok := n.(*parse.NumberNode); ok && n.IsUint {
+ value := reflect.New(typ).Elem()
+ value.SetUint(n.Uint64)
+ return value
+ }
+ s.errorf("expected unsigned integer; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {
+ s.at(n)
+ if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {
+ value := reflect.New(typ).Elem()
+ value.SetFloat(n.Float64)
+ return value
+ }
+ s.errorf("expected float; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {
+ if n, ok := n.(*parse.NumberNode); ok && n.IsComplex {
+ value := reflect.New(typ).Elem()
+ value.SetComplex(n.Complex128)
+ return value
+ }
+ s.errorf("expected complex; found %s", n)
+ panic("not reached")
+}
+
+func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {
+ s.at(n)
+ switch n := n.(type) {
+ case *parse.BoolNode:
+ return reflect.ValueOf(n.True)
+ case *parse.DotNode:
+ return dot
+ case *parse.FieldNode:
+ return s.evalFieldNode(dot, n, nil, zero)
+ case *parse.IdentifierNode:
+ return s.evalFunction(dot, n, n, nil, zero)
+ case *parse.NilNode:
+ // NilNode is handled in evalArg, the only place that calls here.
+ s.errorf("evalEmptyInterface: nil (can't happen)")
+ case *parse.NumberNode:
+ return s.idealConstant(n)
+ case *parse.StringNode:
+ return reflect.ValueOf(n.Text)
+ case *parse.VariableNode:
+ return s.evalVariableNode(dot, n, nil, zero)
+ case *parse.PipeNode:
+ return s.evalPipeline(dot, n)
+ }
+ s.errorf("can't handle assignment of %s to empty interface argument", n)
+ panic("not reached")
+}
+
+// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
+// We indirect through pointers and empty interfaces (only) because
+// non-empty interfaces have methods we might need.
+func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
+ for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
+ if v.IsNil() {
+ return v, true
+ }
+ if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
+ break
+ }
+ }
+ return v, false
+}
+
+// printValue writes the textual representation of the value to the output of
+// the template.
+func (s *state) printValue(n parse.Node, v reflect.Value) {
+ s.at(n)
+ iface, ok := printableValue(v)
+ if !ok {
+ s.errorf("can't print %s of type %s", n, v.Type())
+ }
+ fmt.Fprint(s.wr, iface)
+}
+
+// printableValue returns the, possibly indirected, interface value inside v that
+// is best for a call to formatted printer.
+func printableValue(v reflect.Value) (interface{}, bool) {
+ if v.Kind() == reflect.Ptr {
+ v, _ = indirect(v) // fmt.Fprint handles nil.
+ }
+ if !v.IsValid() {
+ return "<no value>", true
+ }
+
+ if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {
+ if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) {
+ v = v.Addr()
+ } else {
+ switch v.Kind() {
+ case reflect.Chan, reflect.Func:
+ return nil, false
+ }
+ }
+ }
+ return v.Interface(), true
+}
+
+// Types to help sort the keys in a map for reproducible output.
+
+type rvs []reflect.Value
+
+func (x rvs) Len() int { return len(x) }
+func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+type rvInts struct{ rvs }
+
+func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() }
+
+type rvUints struct{ rvs }
+
+func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() }
+
+type rvFloats struct{ rvs }
+
+func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() }
+
+type rvStrings struct{ rvs }
+
+func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() }
+
+// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys.
+func sortKeys(v []reflect.Value) []reflect.Value {
+ if len(v) <= 1 {
+ return v
+ }
+ switch v[0].Kind() {
+ case reflect.Float32, reflect.Float64:
+ sort.Sort(rvFloats{v})
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ sort.Sort(rvInts{v})
+ case reflect.String:
+ sort.Sort(rvStrings{v})
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ sort.Sort(rvUints{v})
+ }
+ return v
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net/url"
+ "reflect"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// FuncMap is the type of the map defining the mapping from names to functions.
+// Each function must have either a single return value, or two return values of
+// which the second has type error. In that case, if the second (error)
+// return value evaluates to non-nil during execution, execution terminates and
+// Execute returns that error.
+type FuncMap map[string]interface{}
+
+var builtins = FuncMap{
+ "and": and,
+ "call": call,
+ "html": HTMLEscaper,
+ "index": index,
+ "js": JSEscaper,
+ "len": length,
+ "not": not,
+ "or": or,
+ "print": fmt.Sprint,
+ "printf": fmt.Sprintf,
+ "println": fmt.Sprintln,
+ "urlquery": URLQueryEscaper,
+
+ // Comparisons
+ "eq": eq, // ==
+ "ge": ge, // >=
+ "gt": gt, // >
+ "le": le, // <=
+ "lt": lt, // <
+ "ne": ne, // !=
+}
+
+var builtinFuncs = createValueFuncs(builtins)
+
+// createValueFuncs turns a FuncMap into a map[string]reflect.Value
+func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
+ m := make(map[string]reflect.Value)
+ addValueFuncs(m, funcMap)
+ return m
+}
+
+// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
+func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
+ for name, fn := range in {
+ v := reflect.ValueOf(fn)
+ if v.Kind() != reflect.Func {
+ panic("value for " + name + " not a function")
+ }
+ if !goodFunc(v.Type()) {
+ panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut()))
+ }
+ out[name] = v
+ }
+}
+
+// addFuncs adds to values the functions in funcs. It does no checking of the input -
+// call addValueFuncs first.
+func addFuncs(out, in FuncMap) {
+ for name, fn := range in {
+ out[name] = fn
+ }
+}
+
+// goodFunc checks that the function or method has the right result signature.
+func goodFunc(typ reflect.Type) bool {
+ // We allow functions with 1 result or 2 results where the second is an error.
+ switch {
+ case typ.NumOut() == 1:
+ return true
+ case typ.NumOut() == 2 && typ.Out(1) == errorType:
+ return true
+ }
+ return false
+}
+
+// findFunction looks for a function in the template, and global map.
+func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
+ if tmpl != nil && tmpl.common != nil {
+ if fn := tmpl.execFuncs[name]; fn.IsValid() {
+ return fn, true
+ }
+ }
+ if fn := builtinFuncs[name]; fn.IsValid() {
+ return fn, true
+ }
+ return reflect.Value{}, false
+}
+
+// Indexing.
+
+// index returns the result of indexing its first argument by the following
+// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
+// indexed item must be a map, slice, or array.
+func index(item interface{}, indices ...interface{}) (interface{}, error) {
+ v := reflect.ValueOf(item)
+ for _, i := range indices {
+ index := reflect.ValueOf(i)
+ var isNil bool
+ if v, isNil = indirect(v); isNil {
+ return nil, fmt.Errorf("index of nil pointer")
+ }
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.String:
+ var x int64
+ switch index.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x = index.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ x = int64(index.Uint())
+ default:
+ return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
+ }
+ if x < 0 || x >= int64(v.Len()) {
+ return nil, fmt.Errorf("index out of range: %d", x)
+ }
+ v = v.Index(int(x))
+ case reflect.Map:
+ if !index.IsValid() {
+ index = reflect.Zero(v.Type().Key())
+ }
+ if !index.Type().AssignableTo(v.Type().Key()) {
+ return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
+ }
+ if x := v.MapIndex(index); x.IsValid() {
+ v = x
+ } else {
+ v = reflect.Zero(v.Type().Elem())
+ }
+ default:
+ return nil, fmt.Errorf("can't index item of type %s", v.Type())
+ }
+ }
+ return v.Interface(), nil
+}
+
+// Length
+
+// length returns the length of the item, with an error if it has no defined length.
+func length(item interface{}) (int, error) {
+ v, isNil := indirect(reflect.ValueOf(item))
+ if isNil {
+ return 0, fmt.Errorf("len of nil pointer")
+ }
+ switch v.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len(), nil
+ }
+ return 0, fmt.Errorf("len of type %s", v.Type())
+}
+
+// Function invocation
+
+// call returns the result of evaluating the first argument as a function.
+// The function must return 1 result, or 2 results, the second of which is an error.
+func call(fn interface{}, args ...interface{}) (interface{}, error) {
+ v := reflect.ValueOf(fn)
+ typ := v.Type()
+ if typ.Kind() != reflect.Func {
+ return nil, fmt.Errorf("non-function of type %s", typ)
+ }
+ if !goodFunc(typ) {
+ return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
+ }
+ numIn := typ.NumIn()
+ var dddType reflect.Type
+ if typ.IsVariadic() {
+ if len(args) < numIn-1 {
+ return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
+ }
+ dddType = typ.In(numIn - 1).Elem()
+ } else {
+ if len(args) != numIn {
+ return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
+ }
+ }
+ argv := make([]reflect.Value, len(args))
+ for i, arg := range args {
+ value := reflect.ValueOf(arg)
+ // Compute the expected type. Clumsy because of variadics.
+ var argType reflect.Type
+ if !typ.IsVariadic() || i < numIn-1 {
+ argType = typ.In(i)
+ } else {
+ argType = dddType
+ }
+ if !value.IsValid() && canBeNil(argType) {
+ value = reflect.Zero(argType)
+ }
+ if !value.Type().AssignableTo(argType) {
+ return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
+ }
+ argv[i] = value
+ }
+ result := v.Call(argv)
+ if len(result) == 2 && !result[1].IsNil() {
+ return result[0].Interface(), result[1].Interface().(error)
+ }
+ return result[0].Interface(), nil
+}
+
+// Boolean logic.
+
+func truth(a interface{}) bool {
+ t, _ := isTrue(reflect.ValueOf(a))
+ return t
+}
+
+// and computes the Boolean AND of its arguments, returning
+// the first false argument it encounters, or the last argument.
+func and(arg0 interface{}, args ...interface{}) interface{} {
+ if !truth(arg0) {
+ return arg0
+ }
+ for i := range args {
+ arg0 = args[i]
+ if !truth(arg0) {
+ break
+ }
+ }
+ return arg0
+}
+
+// or computes the Boolean OR of its arguments, returning
+// the first true argument it encounters, or the last argument.
+func or(arg0 interface{}, args ...interface{}) interface{} {
+ if truth(arg0) {
+ return arg0
+ }
+ for i := range args {
+ arg0 = args[i]
+ if truth(arg0) {
+ break
+ }
+ }
+ return arg0
+}
+
+// not returns the Boolean negation of its argument.
+func not(arg interface{}) (truth bool) {
+ truth, _ = isTrue(reflect.ValueOf(arg))
+ return !truth
+}
+
+// Comparison.
+
+// TODO: Perhaps allow comparison between signed and unsigned integers.
+
+var (
+ errBadComparisonType = errors.New("invalid type for comparison")
+ errBadComparison = errors.New("incompatible types for comparison")
+ errNoComparison = errors.New("missing argument for comparison")
+)
+
+type kind int
+
+const (
+ invalidKind kind = iota
+ boolKind
+ complexKind
+ intKind
+ floatKind
+ integerKind
+ stringKind
+ uintKind
+)
+
+func basicKind(v reflect.Value) (kind, error) {
+ switch v.Kind() {
+ case reflect.Bool:
+ return boolKind, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return intKind, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return uintKind, nil
+ case reflect.Float32, reflect.Float64:
+ return floatKind, nil
+ case reflect.Complex64, reflect.Complex128:
+ return complexKind, nil
+ case reflect.String:
+ return stringKind, nil
+ }
+ return invalidKind, errBadComparisonType
+}
+
+// eq evaluates the comparison a == b || a == c || ...
+func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
+ v1 := reflect.ValueOf(arg1)
+ k1, err := basicKind(v1)
+ if err != nil {
+ return false, err
+ }
+ if len(arg2) == 0 {
+ return false, errNoComparison
+ }
+ for _, arg := range arg2 {
+ v2 := reflect.ValueOf(arg)
+ k2, err := basicKind(v2)
+ if err != nil {
+ return false, err
+ }
+ truth := false
+ if k1 != k2 {
+ // Special case: Can compare integer values regardless of type's sign.
+ switch {
+ case k1 == intKind && k2 == uintKind:
+ truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
+ case k1 == uintKind && k2 == intKind:
+ truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
+ default:
+ return false, errBadComparison
+ }
+ } else {
+ switch k1 {
+ case boolKind:
+ truth = v1.Bool() == v2.Bool()
+ case complexKind:
+ truth = v1.Complex() == v2.Complex()
+ case floatKind:
+ truth = v1.Float() == v2.Float()
+ case intKind:
+ truth = v1.Int() == v2.Int()
+ case stringKind:
+ truth = v1.String() == v2.String()
+ case uintKind:
+ truth = v1.Uint() == v2.Uint()
+ default:
+ panic("invalid kind")
+ }
+ }
+ if truth {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+// ne evaluates the comparison a != b.
+func ne(arg1, arg2 interface{}) (bool, error) {
+ // != is the inverse of ==.
+ equal, err := eq(arg1, arg2)
+ return !equal, err
+}
+
+// lt evaluates the comparison a < b.
+func lt(arg1, arg2 interface{}) (bool, error) {
+ v1 := reflect.ValueOf(arg1)
+ k1, err := basicKind(v1)
+ if err != nil {
+ return false, err
+ }
+ v2 := reflect.ValueOf(arg2)
+ k2, err := basicKind(v2)
+ if err != nil {
+ return false, err
+ }
+ truth := false
+ if k1 != k2 {
+ // Special case: Can compare integer values regardless of type's sign.
+ switch {
+ case k1 == intKind && k2 == uintKind:
+ truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint()
+ case k1 == uintKind && k2 == intKind:
+ truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int())
+ default:
+ return false, errBadComparison
+ }
+ } else {
+ switch k1 {
+ case boolKind, complexKind:
+ return false, errBadComparisonType
+ case floatKind:
+ truth = v1.Float() < v2.Float()
+ case intKind:
+ truth = v1.Int() < v2.Int()
+ case stringKind:
+ truth = v1.String() < v2.String()
+ case uintKind:
+ truth = v1.Uint() < v2.Uint()
+ default:
+ panic("invalid kind")
+ }
+ }
+ return truth, nil
+}
+
+// le evaluates the comparison <= b.
+func le(arg1, arg2 interface{}) (bool, error) {
+ // <= is < or ==.
+ lessThan, err := lt(arg1, arg2)
+ if lessThan || err != nil {
+ return lessThan, err
+ }
+ return eq(arg1, arg2)
+}
+
+// gt evaluates the comparison a > b.
+func gt(arg1, arg2 interface{}) (bool, error) {
+ // > is the inverse of <=.
+ lessOrEqual, err := le(arg1, arg2)
+ if err != nil {
+ return false, err
+ }
+ return !lessOrEqual, nil
+}
+
+// ge evaluates the comparison a >= b.
+func ge(arg1, arg2 interface{}) (bool, error) {
+ // >= is the inverse of <.
+ lessThan, err := lt(arg1, arg2)
+ if err != nil {
+ return false, err
+ }
+ return !lessThan, nil
+}
+
+// HTML escaping.
+
+var (
+ htmlQuot = []byte(""") // shorter than """
+ htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
+ htmlAmp = []byte("&")
+ htmlLt = []byte("<")
+ htmlGt = []byte(">")
+)
+
+// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
+func HTMLEscape(w io.Writer, b []byte) {
+ last := 0
+ for i, c := range b {
+ var html []byte
+ switch c {
+ case '"':
+ html = htmlQuot
+ case '\'':
+ html = htmlApos
+ case '&':
+ html = htmlAmp
+ case '<':
+ html = htmlLt
+ case '>':
+ html = htmlGt
+ default:
+ continue
+ }
+ w.Write(b[last:i])
+ w.Write(html)
+ last = i + 1
+ }
+ w.Write(b[last:])
+}
+
+// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
+func HTMLEscapeString(s string) string {
+ // Avoid allocation if we can.
+ if strings.IndexAny(s, `'"&<>`) < 0 {
+ return s
+ }
+ var b bytes.Buffer
+ HTMLEscape(&b, []byte(s))
+ return b.String()
+}
+
+// HTMLEscaper returns the escaped HTML equivalent of the textual
+// representation of its arguments.
+func HTMLEscaper(args ...interface{}) string {
+ return HTMLEscapeString(evalArgs(args))
+}
+
+// JavaScript escaping.
+
+var (
+ jsLowUni = []byte(`\u00`)
+ hex = []byte("0123456789ABCDEF")
+
+ jsBackslash = []byte(`\\`)
+ jsApos = []byte(`\'`)
+ jsQuot = []byte(`\"`)
+ jsLt = []byte(`\x3C`)
+ jsGt = []byte(`\x3E`)
+)
+
+// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
+func JSEscape(w io.Writer, b []byte) {
+ last := 0
+ for i := 0; i < len(b); i++ {
+ c := b[i]
+
+ if !jsIsSpecial(rune(c)) {
+ // fast path: nothing to do
+ continue
+ }
+ w.Write(b[last:i])
+
+ if c < utf8.RuneSelf {
+ // Quotes, slashes and angle brackets get quoted.
+ // Control characters get written as \u00XX.
+ switch c {
+ case '\\':
+ w.Write(jsBackslash)
+ case '\'':
+ w.Write(jsApos)
+ case '"':
+ w.Write(jsQuot)
+ case '<':
+ w.Write(jsLt)
+ case '>':
+ w.Write(jsGt)
+ default:
+ w.Write(jsLowUni)
+ t, b := c>>4, c&0x0f
+ w.Write(hex[t : t+1])
+ w.Write(hex[b : b+1])
+ }
+ } else {
+ // Unicode rune.
+ r, size := utf8.DecodeRune(b[i:])
+ if unicode.IsPrint(r) {
+ w.Write(b[i : i+size])
+ } else {
+ fmt.Fprintf(w, "\\u%04X", r)
+ }
+ i += size - 1
+ }
+ last = i + 1
+ }
+ w.Write(b[last:])
+}
+
+// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
+func JSEscapeString(s string) string {
+ // Avoid allocation if we can.
+ if strings.IndexFunc(s, jsIsSpecial) < 0 {
+ return s
+ }
+ var b bytes.Buffer
+ JSEscape(&b, []byte(s))
+ return b.String()
+}
+
+func jsIsSpecial(r rune) bool {
+ switch r {
+ case '\\', '\'', '"', '<', '>':
+ return true
+ }
+ return r < ' ' || utf8.RuneSelf <= r
+}
+
+// JSEscaper returns the escaped JavaScript equivalent of the textual
+// representation of its arguments.
+func JSEscaper(args ...interface{}) string {
+ return JSEscapeString(evalArgs(args))
+}
+
+// URLQueryEscaper returns the escaped value of the textual representation of
+// its arguments in a form suitable for embedding in a URL query.
+func URLQueryEscaper(args ...interface{}) string {
+ return url.QueryEscape(evalArgs(args))
+}
+
+// evalArgs formats the list of arguments into a string. It is therefore equivalent to
+// fmt.Sprint(args...)
+// except that each argument is indirected (if a pointer), as required,
+// using the same rules as the default string evaluation during template
+// execution.
+func evalArgs(args []interface{}) string {
+ ok := false
+ var s string
+ // Fast path for simple common case.
+ if len(args) == 1 {
+ s, ok = args[0].(string)
+ }
+ if !ok {
+ for i, arg := range args {
+ a, ok := printableValue(reflect.ValueOf(arg))
+ if ok {
+ args[i] = a
+ } // else left fmt do its thing
+ }
+ s = fmt.Sprint(args...)
+ }
+ return s
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Helper functions to make constructing templates easier.
+
+package template
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path/filepath"
+)
+
+// Functions and methods to parse templates.
+
+// Must is a helper that wraps a call to a function returning (*Template, error)
+// and panics if the error is non-nil. It is intended for use in variable
+// initializations such as
+// var t = template.Must(template.New("name").Parse("text"))
+func Must(t *Template, err error) *Template {
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+// ParseFiles creates a new Template and parses the template definitions from
+// the named files. The returned template's name will have the (base) name and
+// (parsed) contents of the first file. There must be at least one file.
+// If an error occurs, parsing stops and the returned *Template is nil.
+func ParseFiles(filenames ...string) (*Template, error) {
+ return parseFiles(nil, filenames...)
+}
+
+// ParseFiles parses the named files and associates the resulting templates with
+// t. If an error occurs, parsing stops and the returned template is nil;
+// otherwise it is t. There must be at least one file.
+func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
+ return parseFiles(t, filenames...)
+}
+
+// parseFiles is the helper for the method and function. If the argument
+// template is nil, it is created from the first file.
+func parseFiles(t *Template, filenames ...string) (*Template, error) {
+ if len(filenames) == 0 {
+ // Not really a problem, but be consistent.
+ return nil, fmt.Errorf("template: no files named in call to ParseFiles")
+ }
+ for _, filename := range filenames {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ s := string(b)
+ name := filepath.Base(filename)
+ // First template becomes return value if not already defined,
+ // and we use that one for subsequent New calls to associate
+ // all the templates together. Also, if this file has the same name
+ // as t, this file becomes the contents of t, so
+ // t, err := New(name).Funcs(xxx).ParseFiles(name)
+ // works. Otherwise we create a new template associated with t.
+ var tmpl *Template
+ if t == nil {
+ t = New(name)
+ }
+ if name == t.Name() {
+ tmpl = t
+ } else {
+ tmpl = t.New(name)
+ }
+ _, err = tmpl.Parse(s)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return t, nil
+}
+
+// ParseGlob creates a new Template and parses the template definitions from the
+// files identified by the pattern, which must match at least one file. The
+// returned template will have the (base) name and (parsed) contents of the
+// first file matched by the pattern. ParseGlob is equivalent to calling
+// ParseFiles with the list of files matched by the pattern.
+func ParseGlob(pattern string) (*Template, error) {
+ return parseGlob(nil, pattern)
+}
+
+// ParseGlob parses the template definitions in the files identified by the
+// pattern and associates the resulting templates with t. The pattern is
+// processed by filepath.Glob and must match at least one file. ParseGlob is
+// equivalent to calling t.ParseFiles with the list of files matched by the
+// pattern.
+func (t *Template) ParseGlob(pattern string) (*Template, error) {
+ return parseGlob(t, pattern)
+}
+
+// parseGlob is the implementation of the function and method ParseGlob.
+func parseGlob(t *Template, pattern string) (*Template, error) {
+ filenames, err := filepath.Glob(pattern)
+ if err != nil {
+ return nil, err
+ }
+ if len(filenames) == 0 {
+ return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
+ }
+ return parseFiles(t, filenames...)
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package parse
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// item represents a token or text string returned from the scanner.
+type item struct {
+ typ itemType // The type of this item.
+ pos Pos // The starting position, in bytes, of this item in the input string.
+ val string // The value of this item.
+}
+
+func (i item) String() string {
+ switch {
+ case i.typ == itemEOF:
+ return "EOF"
+ case i.typ == itemError:
+ return i.val
+ case i.typ > itemKeyword:
+ return fmt.Sprintf("<%s>", i.val)
+ case len(i.val) > 10:
+ return fmt.Sprintf("%.10q...", i.val)
+ }
+ return fmt.Sprintf("%q", i.val)
+}
+
+// itemType identifies the type of lex items.
+type itemType int
+
+const (
+ itemError itemType = iota // error occurred; value is text of error
+ itemBool // boolean constant
+ itemChar // printable ASCII character; grab bag for comma etc.
+ itemCharConstant // character constant
+ itemComplex // complex constant (1+2i); imaginary is just a number
+ itemColonEquals // colon-equals (':=') introducing a declaration
+ itemEOF
+ itemField // alphanumeric identifier starting with '.'
+ itemIdentifier // alphanumeric identifier not starting with '.'
+ itemLeftDelim // left action delimiter
+ itemLeftParen // '(' inside action
+ itemNumber // simple number, including imaginary
+ itemPipe // pipe symbol
+ itemRawString // raw quoted string (includes quotes)
+ itemRightDelim // right action delimiter
+ itemElideNewline // elide newline after right delim
+ itemRightParen // ')' inside action
+ itemSpace // run of spaces separating arguments
+ itemString // quoted string (includes quotes)
+ itemText // plain text
+ itemVariable // variable starting with '$', such as '$' or '$1' or '$hello'
+ // Keywords appear after all the rest.
+ itemKeyword // used only to delimit the keywords
+ itemDot // the cursor, spelled '.'
+ itemDefine // define keyword
+ itemElse // else keyword
+ itemEnd // end keyword
+ itemIf // if keyword
+ itemNil // the untyped nil constant, easiest to treat as a keyword
+ itemRange // range keyword
+ itemTemplate // template keyword
+ itemWith // with keyword
+)
+
+var key = map[string]itemType{
+ ".": itemDot,
+ "define": itemDefine,
+ "else": itemElse,
+ "end": itemEnd,
+ "if": itemIf,
+ "range": itemRange,
+ "nil": itemNil,
+ "template": itemTemplate,
+ "with": itemWith,
+}
+
+const eof = -1
+
+// stateFn represents the state of the scanner as a function that returns the next state.
+type stateFn func(*lexer) stateFn
+
+// lexer holds the state of the scanner.
+type lexer struct {
+ name string // the name of the input; used only for error reports
+ input string // the string being scanned
+ leftDelim string // start of action
+ rightDelim string // end of action
+ state stateFn // the next lexing function to enter
+ pos Pos // current position in the input
+ start Pos // start position of this item
+ width Pos // width of last rune read from input
+ lastPos Pos // position of most recent item returned by nextItem
+ items chan item // channel of scanned items
+ parenDepth int // nesting depth of ( ) exprs
+}
+
+// next returns the next rune in the input.
+func (l *lexer) next() rune {
+ if int(l.pos) >= len(l.input) {
+ l.width = 0
+ return eof
+ }
+ r, w := utf8.DecodeRuneInString(l.input[l.pos:])
+ l.width = Pos(w)
+ l.pos += l.width
+ return r
+}
+
+// peek returns but does not consume the next rune in the input.
+func (l *lexer) peek() rune {
+ r := l.next()
+ l.backup()
+ return r
+}
+
+// backup steps back one rune. Can only be called once per call of next.
+func (l *lexer) backup() {
+ l.pos -= l.width
+}
+
+// emit passes an item back to the client.
+func (l *lexer) emit(t itemType) {
+ l.items <- item{t, l.start, l.input[l.start:l.pos]}
+ l.start = l.pos
+}
+
+// ignore skips over the pending input before this point.
+func (l *lexer) ignore() {
+ l.start = l.pos
+}
+
+// accept consumes the next rune if it's from the valid set.
+func (l *lexer) accept(valid string) bool {
+ if strings.IndexRune(valid, l.next()) >= 0 {
+ return true
+ }
+ l.backup()
+ return false
+}
+
+// acceptRun consumes a run of runes from the valid set.
+func (l *lexer) acceptRun(valid string) {
+ for strings.IndexRune(valid, l.next()) >= 0 {
+ }
+ l.backup()
+}
+
+// lineNumber reports which line we're on, based on the position of
+// the previous item returned by nextItem. Doing it this way
+// means we don't have to worry about peek double counting.
+func (l *lexer) lineNumber() int {
+ return 1 + strings.Count(l.input[:l.lastPos], "\n")
+}
+
+// errorf returns an error token and terminates the scan by passing
+// back a nil pointer that will be the next state, terminating l.nextItem.
+func (l *lexer) errorf(format string, args ...interface{}) stateFn {
+ l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
+ return nil
+}
+
+// nextItem returns the next item from the input.
+func (l *lexer) nextItem() item {
+ item := <-l.items
+ l.lastPos = item.pos
+ return item
+}
+
+// lex creates a new scanner for the input string.
+func lex(name, input, left, right string) *lexer {
+ if left == "" {
+ left = leftDelim
+ }
+ if right == "" {
+ right = rightDelim
+ }
+ l := &lexer{
+ name: name,
+ input: input,
+ leftDelim: left,
+ rightDelim: right,
+ items: make(chan item),
+ }
+ go l.run()
+ return l
+}
+
+// run runs the state machine for the lexer.
+func (l *lexer) run() {
+ for l.state = lexText; l.state != nil; {
+ l.state = l.state(l)
+ }
+}
+
+// state functions
+
+const (
+ leftDelim = "{{"
+ rightDelim = "}}"
+ leftComment = "/*"
+ rightComment = "*/"
+)
+
+// lexText scans until an opening action delimiter, "{{".
+func lexText(l *lexer) stateFn {
+ for {
+ if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
+ if l.pos > l.start {
+ l.emit(itemText)
+ }
+ return lexLeftDelim
+ }
+ if l.next() == eof {
+ break
+ }
+ }
+ // Correctly reached EOF.
+ if l.pos > l.start {
+ l.emit(itemText)
+ }
+ l.emit(itemEOF)
+ return nil
+}
+
+// lexLeftDelim scans the left delimiter, which is known to be present.
+func lexLeftDelim(l *lexer) stateFn {
+ l.pos += Pos(len(l.leftDelim))
+ if strings.HasPrefix(l.input[l.pos:], leftComment) {
+ return lexComment
+ }
+ l.emit(itemLeftDelim)
+ l.parenDepth = 0
+ return lexInsideAction
+}
+
+// lexComment scans a comment. The left comment marker is known to be present.
+func lexComment(l *lexer) stateFn {
+ l.pos += Pos(len(leftComment))
+ i := strings.Index(l.input[l.pos:], rightComment)
+ if i < 0 {
+ return l.errorf("unclosed comment")
+ }
+ l.pos += Pos(i + len(rightComment))
+ if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
+ return l.errorf("comment ends before closing delimiter")
+
+ }
+ l.pos += Pos(len(l.rightDelim))
+ l.ignore()
+ return lexText
+}
+
+// lexRightDelim scans the right delimiter, which is known to be present.
+func lexRightDelim(l *lexer) stateFn {
+ l.pos += Pos(len(l.rightDelim))
+ l.emit(itemRightDelim)
+ if l.peek() == '\\' {
+ l.pos++
+ l.emit(itemElideNewline)
+ }
+ return lexText
+}
+
+// lexInsideAction scans the elements inside action delimiters.
+func lexInsideAction(l *lexer) stateFn {
+ // Either number, quoted string, or identifier.
+ // Spaces separate arguments; runs of spaces turn into itemSpace.
+ // Pipe symbols separate and are emitted.
+ if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) {
+ if l.parenDepth == 0 {
+ return lexRightDelim
+ }
+ return l.errorf("unclosed left paren")
+ }
+ switch r := l.next(); {
+ case r == eof || isEndOfLine(r):
+ return l.errorf("unclosed action")
+ case isSpace(r):
+ return lexSpace
+ case r == ':':
+ if l.next() != '=' {
+ return l.errorf("expected :=")
+ }
+ l.emit(itemColonEquals)
+ case r == '|':
+ l.emit(itemPipe)
+ case r == '"':
+ return lexQuote
+ case r == '`':
+ return lexRawQuote
+ case r == '$':
+ return lexVariable
+ case r == '\'':
+ return lexChar
+ case r == '.':
+ // special look-ahead for ".field" so we don't break l.backup().
+ if l.pos < Pos(len(l.input)) {
+ r := l.input[l.pos]
+ if r < '0' || '9' < r {
+ return lexField
+ }
+ }
+ fallthrough // '.' can start a number.
+ case r == '+' || r == '-' || ('0' <= r && r <= '9'):
+ l.backup()
+ return lexNumber
+ case isAlphaNumeric(r):
+ l.backup()
+ return lexIdentifier
+ case r == '(':
+ l.emit(itemLeftParen)
+ l.parenDepth++
+ return lexInsideAction
+ case r == ')':
+ l.emit(itemRightParen)
+ l.parenDepth--
+ if l.parenDepth < 0 {
+ return l.errorf("unexpected right paren %#U", r)
+ }
+ return lexInsideAction
+ case r <= unicode.MaxASCII && unicode.IsPrint(r):
+ l.emit(itemChar)
+ return lexInsideAction
+ default:
+ return l.errorf("unrecognized character in action: %#U", r)
+ }
+ return lexInsideAction
+}
+
+// lexSpace scans a run of space characters.
+// One space has already been seen.
+func lexSpace(l *lexer) stateFn {
+ for isSpace(l.peek()) {
+ l.next()
+ }
+ l.emit(itemSpace)
+ return lexInsideAction
+}
+
+// lexIdentifier scans an alphanumeric.
+func lexIdentifier(l *lexer) stateFn {
+Loop:
+ for {
+ switch r := l.next(); {
+ case isAlphaNumeric(r):
+ // absorb.
+ default:
+ l.backup()
+ word := l.input[l.start:l.pos]
+ if !l.atTerminator() {
+ return l.errorf("bad character %#U", r)
+ }
+ switch {
+ case key[word] > itemKeyword:
+ l.emit(key[word])
+ case word[0] == '.':
+ l.emit(itemField)
+ case word == "true", word == "false":
+ l.emit(itemBool)
+ default:
+ l.emit(itemIdentifier)
+ }
+ break Loop
+ }
+ }
+ return lexInsideAction
+}
+
+// lexField scans a field: .Alphanumeric.
+// The . has been scanned.
+func lexField(l *lexer) stateFn {
+ return lexFieldOrVariable(l, itemField)
+}
+
+// lexVariable scans a Variable: $Alphanumeric.
+// The $ has been scanned.
+func lexVariable(l *lexer) stateFn {
+ if l.atTerminator() { // Nothing interesting follows -> "$".
+ l.emit(itemVariable)
+ return lexInsideAction
+ }
+ return lexFieldOrVariable(l, itemVariable)
+}
+
+// lexVariable scans a field or variable: [.$]Alphanumeric.
+// The . or $ has been scanned.
+func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
+ if l.atTerminator() { // Nothing interesting follows -> "." or "$".
+ if typ == itemVariable {
+ l.emit(itemVariable)
+ } else {
+ l.emit(itemDot)
+ }
+ return lexInsideAction
+ }
+ var r rune
+ for {
+ r = l.next()
+ if !isAlphaNumeric(r) {
+ l.backup()
+ break
+ }
+ }
+ if !l.atTerminator() {
+ return l.errorf("bad character %#U", r)
+ }
+ l.emit(typ)
+ return lexInsideAction
+}
+
+// atTerminator reports whether the input is at valid termination character to
+// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases
+// like "$x+2" not being acceptable without a space, in case we decide one
+// day to implement arithmetic.
+func (l *lexer) atTerminator() bool {
+ r := l.peek()
+ if isSpace(r) || isEndOfLine(r) {
+ return true
+ }
+ switch r {
+ case eof, '.', ',', '|', ':', ')', '(':
+ return true
+ }
+ // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
+ // succeed but should fail) but only in extremely rare cases caused by willfully
+ // bad choice of delimiter.
+ if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
+ return true
+ }
+ return false
+}
+
+// lexChar scans a character constant. The initial quote is already
+// scanned. Syntax checking is done by the parser.
+func lexChar(l *lexer) stateFn {
+Loop:
+ for {
+ switch l.next() {
+ case '\\':
+ if r := l.next(); r != eof && r != '\n' {
+ break
+ }
+ fallthrough
+ case eof, '\n':
+ return l.errorf("unterminated character constant")
+ case '\'':
+ break Loop
+ }
+ }
+ l.emit(itemCharConstant)
+ return lexInsideAction
+}
+
+// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This
+// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
+// and "089" - but when it's wrong the input is invalid and the parser (via
+// strconv) will notice.
+func lexNumber(l *lexer) stateFn {
+ if !l.scanNumber() {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ if sign := l.peek(); sign == '+' || sign == '-' {
+ // Complex: 1+2i. No spaces, must end in 'i'.
+ if !l.scanNumber() || l.input[l.pos-1] != 'i' {
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ l.emit(itemComplex)
+ } else {
+ l.emit(itemNumber)
+ }
+ return lexInsideAction
+}
+
+func (l *lexer) scanNumber() bool {
+ // Optional leading sign.
+ l.accept("+-")
+ // Is it hex?
+ digits := "0123456789"
+ if l.accept("0") && l.accept("xX") {
+ digits = "0123456789abcdefABCDEF"
+ }
+ l.acceptRun(digits)
+ if l.accept(".") {
+ l.acceptRun(digits)
+ }
+ if l.accept("eE") {
+ l.accept("+-")
+ l.acceptRun("0123456789")
+ }
+ // Is it imaginary?
+ l.accept("i")
+ // Next thing mustn't be alphanumeric.
+ if isAlphaNumeric(l.peek()) {
+ l.next()
+ return false
+ }
+ return true
+}
+
+// lexQuote scans a quoted string.
+func lexQuote(l *lexer) stateFn {
+Loop:
+ for {
+ switch l.next() {
+ case '\\':
+ if r := l.next(); r != eof && r != '\n' {
+ break
+ }
+ fallthrough
+ case eof, '\n':
+ return l.errorf("unterminated quoted string")
+ case '"':
+ break Loop
+ }
+ }
+ l.emit(itemString)
+ return lexInsideAction
+}
+
+// lexRawQuote scans a raw quoted string.
+func lexRawQuote(l *lexer) stateFn {
+Loop:
+ for {
+ switch l.next() {
+ case eof, '\n':
+ return l.errorf("unterminated raw quoted string")
+ case '`':
+ break Loop
+ }
+ }
+ l.emit(itemRawString)
+ return lexInsideAction
+}
+
+// isSpace reports whether r is a space character.
+func isSpace(r rune) bool {
+ return r == ' ' || r == '\t'
+}
+
+// isEndOfLine reports whether r is an end-of-line character.
+func isEndOfLine(r rune) bool {
+ return r == '\r' || r == '\n'
+}
+
+// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
+func isAlphaNumeric(r rune) bool {
+ return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Parse nodes.
+
+package parse
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+var textFormat = "%s" // Changed to "%q" in tests for better error messages.
+
+// A Node is an element in the parse tree. The interface is trivial.
+// The interface contains an unexported method so that only
+// types local to this package can satisfy it.
+type Node interface {
+ Type() NodeType
+ String() string
+ // Copy does a deep copy of the Node and all its components.
+ // To avoid type assertions, some XxxNodes also have specialized
+ // CopyXxx methods that return *XxxNode.
+ Copy() Node
+ Position() Pos // byte position of start of node in full original input string
+ // tree returns the containing *Tree.
+ // It is unexported so all implementations of Node are in this package.
+ tree() *Tree
+}
+
+// NodeType identifies the type of a parse tree node.
+type NodeType int
+
+// Pos represents a byte position in the original input text from which
+// this template was parsed.
+type Pos int
+
+func (p Pos) Position() Pos {
+ return p
+}
+
+// Type returns itself and provides an easy default implementation
+// for embedding in a Node. Embedded in all non-trivial Nodes.
+func (t NodeType) Type() NodeType {
+ return t
+}
+
+const (
+ NodeText NodeType = iota // Plain text.
+ NodeAction // A non-control action such as a field evaluation.
+ NodeBool // A boolean constant.
+ NodeChain // A sequence of field accesses.
+ NodeCommand // An element of a pipeline.
+ NodeDot // The cursor, dot.
+ nodeElse // An else action. Not added to tree.
+ nodeEnd // An end action. Not added to tree.
+ NodeField // A field or method name.
+ NodeIdentifier // An identifier; always a function name.
+ NodeIf // An if action.
+ NodeList // A list of Nodes.
+ NodeNil // An untyped nil constant.
+ NodeNumber // A numerical constant.
+ NodePipe // A pipeline of commands.
+ NodeRange // A range action.
+ NodeString // A string constant.
+ NodeTemplate // A template invocation action.
+ NodeVariable // A $ variable.
+ NodeWith // A with action.
+)
+
+// Nodes.
+
+// ListNode holds a sequence of nodes.
+type ListNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Nodes []Node // The element nodes in lexical order.
+}
+
+func (t *Tree) newList(pos Pos) *ListNode {
+ return &ListNode{tr: t, NodeType: NodeList, Pos: pos}
+}
+
+func (l *ListNode) append(n Node) {
+ l.Nodes = append(l.Nodes, n)
+}
+
+func (l *ListNode) tree() *Tree {
+ return l.tr
+}
+
+func (l *ListNode) String() string {
+ b := new(bytes.Buffer)
+ for _, n := range l.Nodes {
+ fmt.Fprint(b, n)
+ }
+ return b.String()
+}
+
+func (l *ListNode) CopyList() *ListNode {
+ if l == nil {
+ return l
+ }
+ n := l.tr.newList(l.Pos)
+ for _, elem := range l.Nodes {
+ n.append(elem.Copy())
+ }
+ return n
+}
+
+func (l *ListNode) Copy() Node {
+ return l.CopyList()
+}
+
+// TextNode holds plain text.
+type TextNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Text []byte // The text; may span newlines.
+}
+
+func (t *Tree) newText(pos Pos, text string) *TextNode {
+ return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)}
+}
+
+func (t *TextNode) String() string {
+ return fmt.Sprintf(textFormat, t.Text)
+}
+
+func (t *TextNode) tree() *Tree {
+ return t.tr
+}
+
+func (t *TextNode) Copy() Node {
+ return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
+}
+
+// PipeNode holds a pipeline with optional declaration
+type PipeNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Line int // The line number in the input (deprecated; kept for compatibility)
+ Decl []*VariableNode // Variable declarations in lexical order.
+ Cmds []*CommandNode // The commands in lexical order.
+}
+
+func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode {
+ return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl}
+}
+
+func (p *PipeNode) append(command *CommandNode) {
+ p.Cmds = append(p.Cmds, command)
+}
+
+func (p *PipeNode) String() string {
+ s := ""
+ if len(p.Decl) > 0 {
+ for i, v := range p.Decl {
+ if i > 0 {
+ s += ", "
+ }
+ s += v.String()
+ }
+ s += " := "
+ }
+ for i, c := range p.Cmds {
+ if i > 0 {
+ s += " | "
+ }
+ s += c.String()
+ }
+ return s
+}
+
+func (p *PipeNode) tree() *Tree {
+ return p.tr
+}
+
+func (p *PipeNode) CopyPipe() *PipeNode {
+ if p == nil {
+ return p
+ }
+ var decl []*VariableNode
+ for _, d := range p.Decl {
+ decl = append(decl, d.Copy().(*VariableNode))
+ }
+ n := p.tr.newPipeline(p.Pos, p.Line, decl)
+ for _, c := range p.Cmds {
+ n.append(c.Copy().(*CommandNode))
+ }
+ return n
+}
+
+func (p *PipeNode) Copy() Node {
+ return p.CopyPipe()
+}
+
+// ActionNode holds an action (something bounded by delimiters).
+// Control actions have their own nodes; ActionNode represents simple
+// ones such as field evaluations and parenthesized pipelines.
+type ActionNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Line int // The line number in the input (deprecated; kept for compatibility)
+ Pipe *PipeNode // The pipeline in the action.
+}
+
+func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
+ return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe}
+}
+
+func (a *ActionNode) String() string {
+ return fmt.Sprintf("{{%s}}", a.Pipe)
+
+}
+
+func (a *ActionNode) tree() *Tree {
+ return a.tr
+}
+
+func (a *ActionNode) Copy() Node {
+ return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
+
+}
+
+// CommandNode holds a command (a pipeline inside an evaluating action).
+type CommandNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Args []Node // Arguments in lexical order: Identifier, field, or constant.
+}
+
+func (t *Tree) newCommand(pos Pos) *CommandNode {
+ return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos}
+}
+
+func (c *CommandNode) append(arg Node) {
+ c.Args = append(c.Args, arg)
+}
+
+func (c *CommandNode) String() string {
+ s := ""
+ for i, arg := range c.Args {
+ if i > 0 {
+ s += " "
+ }
+ if arg, ok := arg.(*PipeNode); ok {
+ s += "(" + arg.String() + ")"
+ continue
+ }
+ s += arg.String()
+ }
+ return s
+}
+
+func (c *CommandNode) tree() *Tree {
+ return c.tr
+}
+
+func (c *CommandNode) Copy() Node {
+ if c == nil {
+ return c
+ }
+ n := c.tr.newCommand(c.Pos)
+ for _, c := range c.Args {
+ n.append(c.Copy())
+ }
+ return n
+}
+
+// IdentifierNode holds an identifier.
+type IdentifierNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Ident string // The identifier's name.
+}
+
+// NewIdentifier returns a new IdentifierNode with the given identifier name.
+func NewIdentifier(ident string) *IdentifierNode {
+ return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident}
+}
+
+// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature.
+// Chained for convenience.
+// TODO: fix one day?
+func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode {
+ i.Pos = pos
+ return i
+}
+
+// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature.
+// Chained for convenience.
+// TODO: fix one day?
+func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode {
+ i.tr = t
+ return i
+}
+
+func (i *IdentifierNode) String() string {
+ return i.Ident
+}
+
+func (i *IdentifierNode) tree() *Tree {
+ return i.tr
+}
+
+func (i *IdentifierNode) Copy() Node {
+ return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos)
+}
+
+// VariableNode holds a list of variable names, possibly with chained field
+// accesses. The dollar sign is part of the (first) name.
+type VariableNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Ident []string // Variable name and fields in lexical order.
+}
+
+func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
+ return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")}
+}
+
+func (v *VariableNode) String() string {
+ s := ""
+ for i, id := range v.Ident {
+ if i > 0 {
+ s += "."
+ }
+ s += id
+ }
+ return s
+}
+
+func (v *VariableNode) tree() *Tree {
+ return v.tr
+}
+
+func (v *VariableNode) Copy() Node {
+ return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)}
+}
+
+// DotNode holds the special identifier '.'.
+type DotNode struct {
+ NodeType
+ Pos
+ tr *Tree
+}
+
+func (t *Tree) newDot(pos Pos) *DotNode {
+ return &DotNode{tr: t, NodeType: NodeDot, Pos: pos}
+}
+
+func (d *DotNode) Type() NodeType {
+ // Override method on embedded NodeType for API compatibility.
+ // TODO: Not really a problem; could change API without effect but
+ // api tool complains.
+ return NodeDot
+}
+
+func (d *DotNode) String() string {
+ return "."
+}
+
+func (d *DotNode) tree() *Tree {
+ return d.tr
+}
+
+func (d *DotNode) Copy() Node {
+ return d.tr.newDot(d.Pos)
+}
+
+// NilNode holds the special identifier 'nil' representing an untyped nil constant.
+type NilNode struct {
+ NodeType
+ Pos
+ tr *Tree
+}
+
+func (t *Tree) newNil(pos Pos) *NilNode {
+ return &NilNode{tr: t, NodeType: NodeNil, Pos: pos}
+}
+
+func (n *NilNode) Type() NodeType {
+ // Override method on embedded NodeType for API compatibility.
+ // TODO: Not really a problem; could change API without effect but
+ // api tool complains.
+ return NodeNil
+}
+
+func (n *NilNode) String() string {
+ return "nil"
+}
+
+func (n *NilNode) tree() *Tree {
+ return n.tr
+}
+
+func (n *NilNode) Copy() Node {
+ return n.tr.newNil(n.Pos)
+}
+
+// FieldNode holds a field (identifier starting with '.').
+// The names may be chained ('.x.y').
+// The period is dropped from each ident.
+type FieldNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Ident []string // The identifiers in lexical order.
+}
+
+func (t *Tree) newField(pos Pos, ident string) *FieldNode {
+ return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period
+}
+
+func (f *FieldNode) String() string {
+ s := ""
+ for _, id := range f.Ident {
+ s += "." + id
+ }
+ return s
+}
+
+func (f *FieldNode) tree() *Tree {
+ return f.tr
+}
+
+func (f *FieldNode) Copy() Node {
+ return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)}
+}
+
+// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.').
+// The names may be chained ('.x.y').
+// The periods are dropped from each ident.
+type ChainNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Node Node
+ Field []string // The identifiers in lexical order.
+}
+
+func (t *Tree) newChain(pos Pos, node Node) *ChainNode {
+ return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node}
+}
+
+// Add adds the named field (which should start with a period) to the end of the chain.
+func (c *ChainNode) Add(field string) {
+ if len(field) == 0 || field[0] != '.' {
+ panic("no dot in field")
+ }
+ field = field[1:] // Remove leading dot.
+ if field == "" {
+ panic("empty field")
+ }
+ c.Field = append(c.Field, field)
+}
+
+func (c *ChainNode) String() string {
+ s := c.Node.String()
+ if _, ok := c.Node.(*PipeNode); ok {
+ s = "(" + s + ")"
+ }
+ for _, field := range c.Field {
+ s += "." + field
+ }
+ return s
+}
+
+func (c *ChainNode) tree() *Tree {
+ return c.tr
+}
+
+func (c *ChainNode) Copy() Node {
+ return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)}
+}
+
+// BoolNode holds a boolean constant.
+type BoolNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ True bool // The value of the boolean constant.
+}
+
+func (t *Tree) newBool(pos Pos, true bool) *BoolNode {
+ return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true}
+}
+
+func (b *BoolNode) String() string {
+ if b.True {
+ return "true"
+ }
+ return "false"
+}
+
+func (b *BoolNode) tree() *Tree {
+ return b.tr
+}
+
+func (b *BoolNode) Copy() Node {
+ return b.tr.newBool(b.Pos, b.True)
+}
+
+// NumberNode holds a number: signed or unsigned integer, float, or complex.
+// The value is parsed and stored under all the types that can represent the value.
+// This simulates in a small amount of code the behavior of Go's ideal constants.
+type NumberNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ IsInt bool // Number has an integral value.
+ IsUint bool // Number has an unsigned integral value.
+ IsFloat bool // Number has a floating-point value.
+ IsComplex bool // Number is complex.
+ Int64 int64 // The signed integer value.
+ Uint64 uint64 // The unsigned integer value.
+ Float64 float64 // The floating-point value.
+ Complex128 complex128 // The complex value.
+ Text string // The original textual representation from the input.
+}
+
+func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) {
+ n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text}
+ switch typ {
+ case itemCharConstant:
+ rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0])
+ if err != nil {
+ return nil, err
+ }
+ if tail != "'" {
+ return nil, fmt.Errorf("malformed character constant: %s", text)
+ }
+ n.Int64 = int64(rune)
+ n.IsInt = true
+ n.Uint64 = uint64(rune)
+ n.IsUint = true
+ n.Float64 = float64(rune) // odd but those are the rules.
+ n.IsFloat = true
+ return n, nil
+ case itemComplex:
+ // fmt.Sscan can parse the pair, so let it do the work.
+ if _, err := fmt.Sscan(text, &n.Complex128); err != nil {
+ return nil, err
+ }
+ n.IsComplex = true
+ n.simplifyComplex()
+ return n, nil
+ }
+ // Imaginary constants can only be complex unless they are zero.
+ if len(text) > 0 && text[len(text)-1] == 'i' {
+ f, err := strconv.ParseFloat(text[:len(text)-1], 64)
+ if err == nil {
+ n.IsComplex = true
+ n.Complex128 = complex(0, f)
+ n.simplifyComplex()
+ return n, nil
+ }
+ }
+ // Do integer test first so we get 0x123 etc.
+ u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below.
+ if err == nil {
+ n.IsUint = true
+ n.Uint64 = u
+ }
+ i, err := strconv.ParseInt(text, 0, 64)
+ if err == nil {
+ n.IsInt = true
+ n.Int64 = i
+ if i == 0 {
+ n.IsUint = true // in case of -0.
+ n.Uint64 = u
+ }
+ }
+ // If an integer extraction succeeded, promote the float.
+ if n.IsInt {
+ n.IsFloat = true
+ n.Float64 = float64(n.Int64)
+ } else if n.IsUint {
+ n.IsFloat = true
+ n.Float64 = float64(n.Uint64)
+ } else {
+ f, err := strconv.ParseFloat(text, 64)
+ if err == nil {
+ n.IsFloat = true
+ n.Float64 = f
+ // If a floating-point extraction succeeded, extract the int if needed.
+ if !n.IsInt && float64(int64(f)) == f {
+ n.IsInt = true
+ n.Int64 = int64(f)
+ }
+ if !n.IsUint && float64(uint64(f)) == f {
+ n.IsUint = true
+ n.Uint64 = uint64(f)
+ }
+ }
+ }
+ if !n.IsInt && !n.IsUint && !n.IsFloat {
+ return nil, fmt.Errorf("illegal number syntax: %q", text)
+ }
+ return n, nil
+}
+
+// simplifyComplex pulls out any other types that are represented by the complex number.
+// These all require that the imaginary part be zero.
+func (n *NumberNode) simplifyComplex() {
+ n.IsFloat = imag(n.Complex128) == 0
+ if n.IsFloat {
+ n.Float64 = real(n.Complex128)
+ n.IsInt = float64(int64(n.Float64)) == n.Float64
+ if n.IsInt {
+ n.Int64 = int64(n.Float64)
+ }
+ n.IsUint = float64(uint64(n.Float64)) == n.Float64
+ if n.IsUint {
+ n.Uint64 = uint64(n.Float64)
+ }
+ }
+}
+
+func (n *NumberNode) String() string {
+ return n.Text
+}
+
+func (n *NumberNode) tree() *Tree {
+ return n.tr
+}
+
+func (n *NumberNode) Copy() Node {
+ nn := new(NumberNode)
+ *nn = *n // Easy, fast, correct.
+ return nn
+}
+
+// StringNode holds a string constant. The value has been "unquoted".
+type StringNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Quoted string // The original text of the string, with quotes.
+ Text string // The string, after quote processing.
+}
+
+func (t *Tree) newString(pos Pos, orig, text string) *StringNode {
+ return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text}
+}
+
+func (s *StringNode) String() string {
+ return s.Quoted
+}
+
+func (s *StringNode) tree() *Tree {
+ return s.tr
+}
+
+func (s *StringNode) Copy() Node {
+ return s.tr.newString(s.Pos, s.Quoted, s.Text)
+}
+
+// endNode represents an {{end}} action.
+// It does not appear in the final parse tree.
+type endNode struct {
+ NodeType
+ Pos
+ tr *Tree
+}
+
+func (t *Tree) newEnd(pos Pos) *endNode {
+ return &endNode{tr: t, NodeType: nodeEnd, Pos: pos}
+}
+
+func (e *endNode) String() string {
+ return "{{end}}"
+}
+
+func (e *endNode) tree() *Tree {
+ return e.tr
+}
+
+func (e *endNode) Copy() Node {
+ return e.tr.newEnd(e.Pos)
+}
+
+// elseNode represents an {{else}} action. Does not appear in the final tree.
+type elseNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Line int // The line number in the input (deprecated; kept for compatibility)
+}
+
+func (t *Tree) newElse(pos Pos, line int) *elseNode {
+ return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line}
+}
+
+func (e *elseNode) Type() NodeType {
+ return nodeElse
+}
+
+func (e *elseNode) String() string {
+ return "{{else}}"
+}
+
+func (e *elseNode) tree() *Tree {
+ return e.tr
+}
+
+func (e *elseNode) Copy() Node {
+ return e.tr.newElse(e.Pos, e.Line)
+}
+
+// BranchNode is the common representation of if, range, and with.
+type BranchNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Line int // The line number in the input (deprecated; kept for compatibility)
+ Pipe *PipeNode // The pipeline to be evaluated.
+ List *ListNode // What to execute if the value is non-empty.
+ ElseList *ListNode // What to execute if the value is empty (nil if absent).
+}
+
+func (b *BranchNode) String() string {
+ name := ""
+ switch b.NodeType {
+ case NodeIf:
+ name = "if"
+ case NodeRange:
+ name = "range"
+ case NodeWith:
+ name = "with"
+ default:
+ panic("unknown branch type")
+ }
+ if b.ElseList != nil {
+ return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList)
+ }
+ return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List)
+}
+
+func (b *BranchNode) tree() *Tree {
+ return b.tr
+}
+
+func (b *BranchNode) Copy() Node {
+ switch b.NodeType {
+ case NodeIf:
+ return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ case NodeRange:
+ return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ case NodeWith:
+ return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList)
+ default:
+ panic("unknown branch type")
+ }
+}
+
+// IfNode represents an {{if}} action and its commands.
+type IfNode struct {
+ BranchNode
+}
+
+func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode {
+ return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+}
+
+func (i *IfNode) Copy() Node {
+ return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
+}
+
+// RangeNode represents a {{range}} action and its commands.
+type RangeNode struct {
+ BranchNode
+}
+
+func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode {
+ return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+}
+
+func (r *RangeNode) Copy() Node {
+ return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList())
+}
+
+// WithNode represents a {{with}} action and its commands.
+type WithNode struct {
+ BranchNode
+}
+
+func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode {
+ return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}}
+}
+
+func (w *WithNode) Copy() Node {
+ return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList())
+}
+
+// TemplateNode represents a {{template}} action.
+type TemplateNode struct {
+ NodeType
+ Pos
+ tr *Tree
+ Line int // The line number in the input (deprecated; kept for compatibility)
+ Name string // The name of the template (unquoted).
+ Pipe *PipeNode // The command to evaluate as dot for the template.
+}
+
+func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode {
+ return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe}
+}
+
+func (t *TemplateNode) String() string {
+ if t.Pipe == nil {
+ return fmt.Sprintf("{{template %q}}", t.Name)
+ }
+ return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe)
+}
+
+func (t *TemplateNode) tree() *Tree {
+ return t.tr
+}
+
+func (t *TemplateNode) Copy() Node {
+ return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe())
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package parse builds parse trees for templates as defined by text/template
+// and html/template. Clients should use those packages to construct templates
+// rather than this one, which provides shared internal data structures not
+// intended for general use.
+package parse
+
+import (
+ "bytes"
+ "fmt"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+// Tree is the representation of a single parsed template.
+type Tree struct {
+ Name string // name of the template represented by the tree.
+ ParseName string // name of the top-level template during parsing, for error messages.
+ Root *ListNode // top-level root of the tree.
+ text string // text parsed to create the template (or its parent)
+ // Parsing only; cleared after parse.
+ funcs []map[string]interface{}
+ lex *lexer
+ token [3]item // three-token lookahead for parser.
+ peekCount int
+ vars []string // variables defined at the moment.
+}
+
+// Copy returns a copy of the Tree. Any parsing state is discarded.
+func (t *Tree) Copy() *Tree {
+ if t == nil {
+ return nil
+ }
+ return &Tree{
+ Name: t.Name,
+ ParseName: t.ParseName,
+ Root: t.Root.CopyList(),
+ text: t.text,
+ }
+}
+
+// Parse returns a map from template name to parse.Tree, created by parsing the
+// templates described in the argument string. The top-level template will be
+// given the specified name. If an error is encountered, parsing stops and an
+// empty map is returned with the error.
+func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {
+ treeSet = make(map[string]*Tree)
+ t := New(name)
+ t.text = text
+ _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...)
+ return
+}
+
+// next returns the next token.
+func (t *Tree) next() item {
+ if t.peekCount > 0 {
+ t.peekCount--
+ } else {
+ t.token[0] = t.lex.nextItem()
+ }
+ return t.token[t.peekCount]
+}
+
+// backup backs the input stream up one token.
+func (t *Tree) backup() {
+ t.peekCount++
+}
+
+// backup2 backs the input stream up two tokens.
+// The zeroth token is already there.
+func (t *Tree) backup2(t1 item) {
+ t.token[1] = t1
+ t.peekCount = 2
+}
+
+// backup3 backs the input stream up three tokens
+// The zeroth token is already there.
+func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
+ t.token[1] = t1
+ t.token[2] = t2
+ t.peekCount = 3
+}
+
+// peek returns but does not consume the next token.
+func (t *Tree) peek() item {
+ if t.peekCount > 0 {
+ return t.token[t.peekCount-1]
+ }
+ t.peekCount = 1
+ t.token[0] = t.lex.nextItem()
+ return t.token[0]
+}
+
+// nextNonSpace returns the next non-space token.
+func (t *Tree) nextNonSpace() (token item) {
+ for {
+ token = t.next()
+ if token.typ != itemSpace {
+ break
+ }
+ }
+ return token
+}
+
+// peekNonSpace returns but does not consume the next non-space token.
+func (t *Tree) peekNonSpace() (token item) {
+ for {
+ token = t.next()
+ if token.typ != itemSpace {
+ break
+ }
+ }
+ t.backup()
+ return token
+}
+
+// Parsing.
+
+// New allocates a new parse tree with the given name.
+func New(name string, funcs ...map[string]interface{}) *Tree {
+ return &Tree{
+ Name: name,
+ funcs: funcs,
+ }
+}
+
+// ErrorContext returns a textual representation of the location of the node in the input text.
+// The receiver is only used when the node does not have a pointer to the tree inside,
+// which can occur in old code.
+func (t *Tree) ErrorContext(n Node) (location, context string) {
+ pos := int(n.Position())
+ tree := n.tree()
+ if tree == nil {
+ tree = t
+ }
+ text := tree.text[:pos]
+ byteNum := strings.LastIndex(text, "\n")
+ if byteNum == -1 {
+ byteNum = pos // On first line.
+ } else {
+ byteNum++ // After the newline.
+ byteNum = pos - byteNum
+ }
+ lineNum := 1 + strings.Count(text, "\n")
+ context = n.String()
+ if len(context) > 20 {
+ context = fmt.Sprintf("%.20s...", context)
+ }
+ return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context
+}
+
+// errorf formats the error and terminates processing.
+func (t *Tree) errorf(format string, args ...interface{}) {
+ t.Root = nil
+ format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
+ panic(fmt.Errorf(format, args...))
+}
+
+// error terminates processing.
+func (t *Tree) error(err error) {
+ t.errorf("%s", err)
+}
+
+// expect consumes the next token and guarantees it has the required type.
+func (t *Tree) expect(expected itemType, context string) item {
+ token := t.nextNonSpace()
+ if token.typ != expected {
+ t.unexpected(token, context)
+ }
+ return token
+}
+
+// expectOneOf consumes the next token and guarantees it has one of the required types.
+func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
+ token := t.nextNonSpace()
+ if token.typ != expected1 && token.typ != expected2 {
+ t.unexpected(token, context)
+ }
+ return token
+}
+
+// unexpected complains about the token and terminates processing.
+func (t *Tree) unexpected(token item, context string) {
+ t.errorf("unexpected %s in %s", token, context)
+}
+
+// recover is the handler that turns panics into returns from the top level of Parse.
+func (t *Tree) recover(errp *error) {
+ e := recover()
+ if e != nil {
+ if _, ok := e.(runtime.Error); ok {
+ panic(e)
+ }
+ if t != nil {
+ t.stopParse()
+ }
+ *errp = e.(error)
+ }
+ return
+}
+
+// startParse initializes the parser, using the lexer.
+func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {
+ t.Root = nil
+ t.lex = lex
+ t.vars = []string{"$"}
+ t.funcs = funcs
+}
+
+// stopParse terminates parsing.
+func (t *Tree) stopParse() {
+ t.lex = nil
+ t.vars = nil
+ t.funcs = nil
+}
+
+// Parse parses the template definition string to construct a representation of
+// the template for execution. If either action delimiter string is empty, the
+// default ("{{" or "}}") is used. Embedded template definitions are added to
+// the treeSet map.
+func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
+ defer t.recover(&err)
+ t.ParseName = t.Name
+ t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))
+ t.text = text
+ t.parse(treeSet)
+ t.add(treeSet)
+ t.stopParse()
+ return t, nil
+}
+
+// add adds tree to the treeSet.
+func (t *Tree) add(treeSet map[string]*Tree) {
+ tree := treeSet[t.Name]
+ if tree == nil || IsEmptyTree(tree.Root) {
+ treeSet[t.Name] = t
+ return
+ }
+ if !IsEmptyTree(t.Root) {
+ t.errorf("template: multiple definition of template %q", t.Name)
+ }
+}
+
+// IsEmptyTree reports whether this tree (node) is empty of everything but space.
+func IsEmptyTree(n Node) bool {
+ switch n := n.(type) {
+ case nil:
+ return true
+ case *ActionNode:
+ case *IfNode:
+ case *ListNode:
+ for _, node := range n.Nodes {
+ if !IsEmptyTree(node) {
+ return false
+ }
+ }
+ return true
+ case *RangeNode:
+ case *TemplateNode:
+ case *TextNode:
+ return len(bytes.TrimSpace(n.Text)) == 0
+ case *WithNode:
+ default:
+ panic("unknown node: " + n.String())
+ }
+ return false
+}
+
+// parse is the top-level parser for a template, essentially the same
+// as itemList except it also parses {{define}} actions.
+// It runs to EOF.
+func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
+ t.Root = t.newList(t.peek().pos)
+ for t.peek().typ != itemEOF {
+ if t.peek().typ == itemLeftDelim {
+ delim := t.next()
+ if t.nextNonSpace().typ == itemDefine {
+ newT := New("definition") // name will be updated once we know it.
+ newT.text = t.text
+ newT.ParseName = t.ParseName
+ newT.startParse(t.funcs, t.lex)
+ newT.parseDefinition(treeSet)
+ continue
+ }
+ t.backup2(delim)
+ }
+ n := t.textOrAction()
+ if n.Type() == nodeEnd {
+ t.errorf("unexpected %s", n)
+ }
+ t.Root.append(n)
+ }
+ return nil
+}
+
+// parseDefinition parses a {{define}} ... {{end}} template definition and
+// installs the definition in the treeSet map. The "define" keyword has already
+// been scanned.
+func (t *Tree) parseDefinition(treeSet map[string]*Tree) {
+ const context = "define clause"
+ name := t.expectOneOf(itemString, itemRawString, context)
+ var err error
+ t.Name, err = strconv.Unquote(name.val)
+ if err != nil {
+ t.error(err)
+ }
+ t.expect(itemRightDelim, context)
+ var end Node
+ t.Root, end = t.itemList()
+ if end.Type() != nodeEnd {
+ t.errorf("unexpected %s in %s", end, context)
+ }
+ t.add(treeSet)
+ t.stopParse()
+}
+
+// itemList:
+// textOrAction*
+// Terminates at {{end}} or {{else}}, returned separately.
+func (t *Tree) itemList() (list *ListNode, next Node) {
+ list = t.newList(t.peekNonSpace().pos)
+ for t.peekNonSpace().typ != itemEOF {
+ n := t.textOrAction()
+ switch n.Type() {
+ case nodeEnd, nodeElse:
+ return list, n
+ }
+ list.append(n)
+ }
+ t.errorf("unexpected EOF")
+ return
+}
+
+// textOrAction:
+// text | action
+func (t *Tree) textOrAction() Node {
+ switch token := t.nextNonSpace(); token.typ {
+ case itemElideNewline:
+ return t.elideNewline()
+ case itemText:
+ return t.newText(token.pos, token.val)
+ case itemLeftDelim:
+ return t.action()
+ default:
+ t.unexpected(token, "input")
+ }
+ return nil
+}
+
+// elideNewline:
+// Remove newlines trailing rightDelim if \\ is present.
+func (t *Tree) elideNewline() Node {
+ token := t.peek()
+ if token.typ != itemText {
+ t.unexpected(token, "input")
+ return nil
+ }
+
+ t.next()
+ stripped := strings.TrimLeft(token.val, "\n\r")
+ diff := len(token.val) - len(stripped)
+ if diff > 0 {
+ // This is a bit nasty. We mutate the token in-place to remove
+ // preceding newlines.
+ token.pos += Pos(diff)
+ token.val = stripped
+ }
+ return t.newText(token.pos, token.val)
+}
+
+// Action:
+// control
+// command ("|" command)*
+// Left delim is past. Now get actions.
+// First word could be a keyword such as range.
+func (t *Tree) action() (n Node) {
+ switch token := t.nextNonSpace(); token.typ {
+ case itemElse:
+ return t.elseControl()
+ case itemEnd:
+ return t.endControl()
+ case itemIf:
+ return t.ifControl()
+ case itemRange:
+ return t.rangeControl()
+ case itemTemplate:
+ return t.templateControl()
+ case itemWith:
+ return t.withControl()
+ }
+ t.backup()
+ // Do not pop variables; they persist until "end".
+ return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
+}
+
+// Pipeline:
+// declarations? command ('|' command)*
+func (t *Tree) pipeline(context string) (pipe *PipeNode) {
+ var decl []*VariableNode
+ pos := t.peekNonSpace().pos
+ // Are there declarations?
+ for {
+ if v := t.peekNonSpace(); v.typ == itemVariable {
+ t.next()
+ // Since space is a token, we need 3-token look-ahead here in the worst case:
+ // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
+ // argument variable rather than a declaration. So remember the token
+ // adjacent to the variable so we can push it back if necessary.
+ tokenAfterVariable := t.peek()
+ if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") {
+ t.nextNonSpace()
+ variable := t.newVariable(v.pos, v.val)
+ decl = append(decl, variable)
+ t.vars = append(t.vars, v.val)
+ if next.typ == itemChar && next.val == "," {
+ if context == "range" && len(decl) < 2 {
+ continue
+ }
+ t.errorf("too many declarations in %s", context)
+ }
+ } else if tokenAfterVariable.typ == itemSpace {
+ t.backup3(v, tokenAfterVariable)
+ } else {
+ t.backup2(v)
+ }
+ }
+ break
+ }
+ pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
+ for {
+ switch token := t.nextNonSpace(); token.typ {
+ case itemRightDelim, itemRightParen:
+ if len(pipe.Cmds) == 0 {
+ t.errorf("missing value for %s", context)
+ }
+ if token.typ == itemRightParen {
+ t.backup()
+ }
+ return
+ case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
+ itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
+ t.backup()
+ pipe.append(t.command())
+ default:
+ t.unexpected(token, context)
+ }
+ }
+}
+
+func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
+ defer t.popVars(len(t.vars))
+ line = t.lex.lineNumber()
+ pipe = t.pipeline(context)
+ var next Node
+ list, next = t.itemList()
+ switch next.Type() {
+ case nodeEnd: //done
+ case nodeElse:
+ if allowElseIf {
+ // Special case for "else if". If the "else" is followed immediately by an "if",
+ // the elseControl will have left the "if" token pending. Treat
+ // {{if a}}_{{else if b}}_{{end}}
+ // as
+ // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}.
+ // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}}
+ // is assumed. This technique works even for long if-else-if chains.
+ // TODO: Should we allow else-if in with and range?
+ if t.peek().typ == itemIf {
+ t.next() // Consume the "if" token.
+ elseList = t.newList(next.Position())
+ elseList.append(t.ifControl())
+ // Do not consume the next item - only one {{end}} required.
+ break
+ }
+ }
+ elseList, next = t.itemList()
+ if next.Type() != nodeEnd {
+ t.errorf("expected end; found %s", next)
+ }
+ }
+ return pipe.Position(), line, pipe, list, elseList
+}
+
+// If:
+// {{if pipeline}} itemList {{end}}
+// {{if pipeline}} itemList {{else}} itemList {{end}}
+// If keyword is past.
+func (t *Tree) ifControl() Node {
+ return t.newIf(t.parseControl(true, "if"))
+}
+
+// Range:
+// {{range pipeline}} itemList {{end}}
+// {{range pipeline}} itemList {{else}} itemList {{end}}
+// Range keyword is past.
+func (t *Tree) rangeControl() Node {
+ return t.newRange(t.parseControl(false, "range"))
+}
+
+// With:
+// {{with pipeline}} itemList {{end}}
+// {{with pipeline}} itemList {{else}} itemList {{end}}
+// If keyword is past.
+func (t *Tree) withControl() Node {
+ return t.newWith(t.parseControl(false, "with"))
+}
+
+// End:
+// {{end}}
+// End keyword is past.
+func (t *Tree) endControl() Node {
+ return t.newEnd(t.expect(itemRightDelim, "end").pos)
+}
+
+// Else:
+// {{else}}
+// Else keyword is past.
+func (t *Tree) elseControl() Node {
+ // Special case for "else if".
+ peek := t.peekNonSpace()
+ if peek.typ == itemIf {
+ // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
+ return t.newElse(peek.pos, t.lex.lineNumber())
+ }
+ return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
+}
+
+// Template:
+// {{template stringValue pipeline}}
+// Template keyword is past. The name must be something that can evaluate
+// to a string.
+func (t *Tree) templateControl() Node {
+ var name string
+ token := t.nextNonSpace()
+ switch token.typ {
+ case itemString, itemRawString:
+ s, err := strconv.Unquote(token.val)
+ if err != nil {
+ t.error(err)
+ }
+ name = s
+ default:
+ t.unexpected(token, "template invocation")
+ }
+ var pipe *PipeNode
+ if t.nextNonSpace().typ != itemRightDelim {
+ t.backup()
+ // Do not pop variables; they persist until "end".
+ pipe = t.pipeline("template")
+ }
+ return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
+}
+
+// command:
+// operand (space operand)*
+// space-separated arguments up to a pipeline character or right delimiter.
+// we consume the pipe character but leave the right delim to terminate the action.
+func (t *Tree) command() *CommandNode {
+ cmd := t.newCommand(t.peekNonSpace().pos)
+ for {
+ t.peekNonSpace() // skip leading spaces.
+ operand := t.operand()
+ if operand != nil {
+ cmd.append(operand)
+ }
+ switch token := t.next(); token.typ {
+ case itemSpace:
+ continue
+ case itemError:
+ t.errorf("%s", token.val)
+ case itemRightDelim, itemRightParen:
+ t.backup()
+ case itemPipe:
+ default:
+ t.errorf("unexpected %s in operand; missing space?", token)
+ }
+ break
+ }
+ if len(cmd.Args) == 0 {
+ t.errorf("empty command")
+ }
+ return cmd
+}
+
+// operand:
+// term .Field*
+// An operand is a space-separated component of a command,
+// a term possibly followed by field accesses.
+// A nil return means the next item is not an operand.
+func (t *Tree) operand() Node {
+ node := t.term()
+ if node == nil {
+ return nil
+ }
+ if t.peek().typ == itemField {
+ chain := t.newChain(t.peek().pos, node)
+ for t.peek().typ == itemField {
+ chain.Add(t.next().val)
+ }
+ // Compatibility with original API: If the term is of type NodeField
+ // or NodeVariable, just put more fields on the original.
+ // Otherwise, keep the Chain node.
+ // TODO: Switch to Chains always when we can.
+ switch node.Type() {
+ case NodeField:
+ node = t.newField(chain.Position(), chain.String())
+ case NodeVariable:
+ node = t.newVariable(chain.Position(), chain.String())
+ default:
+ node = chain
+ }
+ }
+ return node
+}
+
+// term:
+// literal (number, string, nil, boolean)
+// function (identifier)
+// .
+// .Field
+// $
+// '(' pipeline ')'
+// A term is a simple "expression".
+// A nil return means the next item is not a term.
+func (t *Tree) term() Node {
+ switch token := t.nextNonSpace(); token.typ {
+ case itemError:
+ t.errorf("%s", token.val)
+ case itemIdentifier:
+ if !t.hasFunction(token.val) {
+ t.errorf("function %q not defined", token.val)
+ }
+ return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
+ case itemDot:
+ return t.newDot(token.pos)
+ case itemNil:
+ return t.newNil(token.pos)
+ case itemVariable:
+ return t.useVar(token.pos, token.val)
+ case itemField:
+ return t.newField(token.pos, token.val)
+ case itemBool:
+ return t.newBool(token.pos, token.val == "true")
+ case itemCharConstant, itemComplex, itemNumber:
+ number, err := t.newNumber(token.pos, token.val, token.typ)
+ if err != nil {
+ t.error(err)
+ }
+ return number
+ case itemLeftParen:
+ pipe := t.pipeline("parenthesized pipeline")
+ if token := t.next(); token.typ != itemRightParen {
+ t.errorf("unclosed right paren: unexpected %s", token)
+ }
+ return pipe
+ case itemString, itemRawString:
+ s, err := strconv.Unquote(token.val)
+ if err != nil {
+ t.error(err)
+ }
+ return t.newString(token.pos, token.val, s)
+ }
+ t.backup()
+ return nil
+}
+
+// hasFunction reports if a function name exists in the Tree's maps.
+func (t *Tree) hasFunction(name string) bool {
+ for _, funcMap := range t.funcs {
+ if funcMap == nil {
+ continue
+ }
+ if funcMap[name] != nil {
+ return true
+ }
+ }
+ return false
+}
+
+// popVars trims the variable list to the specified length
+func (t *Tree) popVars(n int) {
+ t.vars = t.vars[:n]
+}
+
+// useVar returns a node for a variable reference. It errors if the
+// variable is not defined.
+func (t *Tree) useVar(pos Pos, name string) Node {
+ v := t.newVariable(pos, name)
+ for _, varName := range t.vars {
+ if varName == v.Ident[0] {
+ return v
+ }
+ }
+ t.errorf("undefined variable %q", v.Ident[0])
+ return nil
+}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/alecthomas/template/parse"
+)
+
+// common holds the information shared by related templates.
+type common struct {
+ tmpl map[string]*Template
+ // We use two maps, one for parsing and one for execution.
+ // This separation makes the API cleaner since it doesn't
+ // expose reflection to the client.
+ parseFuncs FuncMap
+ execFuncs map[string]reflect.Value
+}
+
+// Template is the representation of a parsed template. The *parse.Tree
+// field is exported only for use by html/template and should be treated
+// as unexported by all other clients.
+type Template struct {
+ name string
+ *parse.Tree
+ *common
+ leftDelim string
+ rightDelim string
+}
+
+// New allocates a new template with the given name.
+func New(name string) *Template {
+ return &Template{
+ name: name,
+ }
+}
+
+// Name returns the name of the template.
+func (t *Template) Name() string {
+ return t.name
+}
+
+// New allocates a new template associated with the given one and with the same
+// delimiters. The association, which is transitive, allows one template to
+// invoke another with a {{template}} action.
+func (t *Template) New(name string) *Template {
+ t.init()
+ return &Template{
+ name: name,
+ common: t.common,
+ leftDelim: t.leftDelim,
+ rightDelim: t.rightDelim,
+ }
+}
+
+func (t *Template) init() {
+ if t.common == nil {
+ t.common = new(common)
+ t.tmpl = make(map[string]*Template)
+ t.parseFuncs = make(FuncMap)
+ t.execFuncs = make(map[string]reflect.Value)
+ }
+}
+
+// Clone returns a duplicate of the template, including all associated
+// templates. The actual representation is not copied, but the name space of
+// associated templates is, so further calls to Parse in the copy will add
+// templates to the copy but not to the original. Clone can be used to prepare
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+func (t *Template) Clone() (*Template, error) {
+ nt := t.copy(nil)
+ nt.init()
+ nt.tmpl[t.name] = nt
+ for k, v := range t.tmpl {
+ if k == t.name { // Already installed.
+ continue
+ }
+ // The associated templates share nt's common structure.
+ tmpl := v.copy(nt.common)
+ nt.tmpl[k] = tmpl
+ }
+ for k, v := range t.parseFuncs {
+ nt.parseFuncs[k] = v
+ }
+ for k, v := range t.execFuncs {
+ nt.execFuncs[k] = v
+ }
+ return nt, nil
+}
+
+// copy returns a shallow copy of t, with common set to the argument.
+func (t *Template) copy(c *common) *Template {
+ nt := New(t.name)
+ nt.Tree = t.Tree
+ nt.common = c
+ nt.leftDelim = t.leftDelim
+ nt.rightDelim = t.rightDelim
+ return nt
+}
+
+// AddParseTree creates a new template with the name and parse tree
+// and associates it with t.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+ if t.common != nil && t.tmpl[name] != nil {
+ return nil, fmt.Errorf("template: redefinition of template %q", name)
+ }
+ nt := t.New(name)
+ nt.Tree = tree
+ t.tmpl[name] = nt
+ return nt, nil
+}
+
+// Templates returns a slice of the templates associated with t, including t
+// itself.
+func (t *Template) Templates() []*Template {
+ if t.common == nil {
+ return nil
+ }
+ // Return a slice so we don't expose the map.
+ m := make([]*Template, 0, len(t.tmpl))
+ for _, v := range t.tmpl {
+ m = append(m, v)
+ }
+ return m
+}
+
+// Delims sets the action delimiters to the specified strings, to be used in
+// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
+// definitions will inherit the settings. An empty delimiter stands for the
+// corresponding default: {{ or }}.
+// The return value is the template, so calls can be chained.
+func (t *Template) Delims(left, right string) *Template {
+ t.leftDelim = left
+ t.rightDelim = right
+ return t
+}
+
+// Funcs adds the elements of the argument map to the template's function map.
+// It panics if a value in the map is not a function with appropriate return
+// type. However, it is legal to overwrite elements of the map. The return
+// value is the template, so calls can be chained.
+func (t *Template) Funcs(funcMap FuncMap) *Template {
+ t.init()
+ addValueFuncs(t.execFuncs, funcMap)
+ addFuncs(t.parseFuncs, funcMap)
+ return t
+}
+
+// Lookup returns the template with the given name that is associated with t,
+// or nil if there is no such template.
+func (t *Template) Lookup(name string) *Template {
+ if t.common == nil {
+ return nil
+ }
+ return t.tmpl[name]
+}
+
+// Parse parses a string into a template. Nested template definitions will be
+// associated with the top-level template t. Parse may be called multiple times
+// to parse definitions of templates to associate with t. It is an error if a
+// resulting template is non-empty (contains content other than template
+// definitions) and would replace a non-empty template with the same name.
+// (In multiple calls to Parse with the same receiver template, only one call
+// can contain text other than space, comments, and template definitions.)
+func (t *Template) Parse(text string) (*Template, error) {
+ t.init()
+ trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+ if err != nil {
+ return nil, err
+ }
+ // Add the newly parsed trees, including the one for t, into our common structure.
+ for name, tree := range trees {
+ // If the name we parsed is the name of this template, overwrite this template.
+ // The associate method checks it's not a redefinition.
+ tmpl := t
+ if name != t.name {
+ tmpl = t.New(name)
+ }
+ // Even if t == tmpl, we need to install it in the common.tmpl map.
+ if replace, err := t.associate(tmpl, tree); err != nil {
+ return nil, err
+ } else if replace {
+ tmpl.Tree = tree
+ }
+ tmpl.leftDelim = t.leftDelim
+ tmpl.rightDelim = t.rightDelim
+ }
+ return t, nil
+}
+
+// associate installs the new template into the group of templates associated
+// with t. It is an error to reuse a name except to overwrite an empty
+// template. The two are already known to share the common structure.
+// The boolean return value reports wither to store this tree as t.Tree.
+func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) {
+ if new.common != t.common {
+ panic("internal error: associate not common")
+ }
+ name := new.name
+ if old := t.tmpl[name]; old != nil {
+ oldIsEmpty := parse.IsEmptyTree(old.Root)
+ newIsEmpty := parse.IsEmptyTree(tree.Root)
+ if newIsEmpty {
+ // Whether old is empty or not, new is empty; no reason to replace old.
+ return false, nil
+ }
+ if !oldIsEmpty {
+ return false, fmt.Errorf("template: redefinition of template %q", name)
+ }
+ }
+ t.tmpl[name] = new
+ return true, nil
+}
--- /dev/null
+Copyright (C) 2014 Alec Thomas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# Units - Helpful unit multipliers and functions for Go
+
+The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
+
+It allows for code like this:
+
+```go
+n, err := ParseBase2Bytes("1KB")
+// n == 1024
+n = units.Mebibyte * 512
+```
--- /dev/null
+package units
+
+// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte,
+// etc.).
+type Base2Bytes int64
+
+// Base-2 byte units.
+const (
+ Kibibyte Base2Bytes = 1024
+ KiB = Kibibyte
+ Mebibyte = Kibibyte * 1024
+ MiB = Mebibyte
+ Gibibyte = Mebibyte * 1024
+ GiB = Gibibyte
+ Tebibyte = Gibibyte * 1024
+ TiB = Tebibyte
+ Pebibyte = Tebibyte * 1024
+ PiB = Pebibyte
+ Exbibyte = Pebibyte * 1024
+ EiB = Exbibyte
+)
+
+var (
+ bytesUnitMap = MakeUnitMap("iB", "B", 1024)
+ oldBytesUnitMap = MakeUnitMap("B", "B", 1024)
+)
+
+// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB
+// and KiB are both 1024.
+func ParseBase2Bytes(s string) (Base2Bytes, error) {
+ n, err := ParseUnit(s, bytesUnitMap)
+ if err != nil {
+ n, err = ParseUnit(s, oldBytesUnitMap)
+ }
+ return Base2Bytes(n), err
+}
+
+func (b Base2Bytes) String() string {
+ return ToString(int64(b), 1024, "iB", "B")
+}
+
+var (
+ metricBytesUnitMap = MakeUnitMap("B", "B", 1000)
+)
+
+// MetricBytes are SI byte units (1000 bytes in a kilobyte).
+type MetricBytes SI
+
+// SI base-10 byte units.
+const (
+ Kilobyte MetricBytes = 1000
+ KB = Kilobyte
+ Megabyte = Kilobyte * 1000
+ MB = Megabyte
+ Gigabyte = Megabyte * 1000
+ GB = Gigabyte
+ Terabyte = Gigabyte * 1000
+ TB = Terabyte
+ Petabyte = Terabyte * 1000
+ PB = Petabyte
+ Exabyte = Petabyte * 1000
+ EB = Exabyte
+)
+
+// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes.
+func ParseMetricBytes(s string) (MetricBytes, error) {
+ n, err := ParseUnit(s, metricBytesUnitMap)
+ return MetricBytes(n), err
+}
+
+func (m MetricBytes) String() string {
+ return ToString(int64(m), 1000, "B", "B")
+}
+
+// ParseStrictBytes supports both iB and B suffixes for base 2 and metric,
+// respectively. That is, KiB represents 1024 and KB represents 1000.
+func ParseStrictBytes(s string) (int64, error) {
+ n, err := ParseUnit(s, bytesUnitMap)
+ if err != nil {
+ n, err = ParseUnit(s, metricBytesUnitMap)
+ }
+ return int64(n), err
+}
--- /dev/null
+// Package units provides helpful unit multipliers and functions for Go.
+//
+// The goal of this package is to have functionality similar to the time [1] package.
+//
+//
+// [1] http://golang.org/pkg/time/
+//
+// It allows for code like this:
+//
+// n, err := ParseBase2Bytes("1KB")
+// // n == 1024
+// n = units.Mebibyte * 512
+package units
--- /dev/null
+package units
+
+// SI units.
+type SI int64
+
+// SI unit multiples.
+const (
+ Kilo SI = 1000
+ Mega = Kilo * 1000
+ Giga = Mega * 1000
+ Tera = Giga * 1000
+ Peta = Tera * 1000
+ Exa = Peta * 1000
+)
+
+func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 {
+ return map[string]float64{
+ shortSuffix: 1,
+ "K" + suffix: float64(scale),
+ "M" + suffix: float64(scale * scale),
+ "G" + suffix: float64(scale * scale * scale),
+ "T" + suffix: float64(scale * scale * scale * scale),
+ "P" + suffix: float64(scale * scale * scale * scale * scale),
+ "E" + suffix: float64(scale * scale * scale * scale * scale * scale),
+ }
+}
--- /dev/null
+package units
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+)
+
+var (
+ siUnits = []string{"", "K", "M", "G", "T", "P", "E"}
+)
+
+func ToString(n int64, scale int64, suffix, baseSuffix string) string {
+ mn := len(siUnits)
+ out := make([]string, mn)
+ for i, m := range siUnits {
+ if n%scale != 0 || i == 0 && n == 0 {
+ s := suffix
+ if i == 0 {
+ s = baseSuffix
+ }
+ out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s)
+ }
+ n /= scale
+ if n == 0 {
+ break
+ }
+ }
+ return strings.Join(out, "")
+}
+
+// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123
+var errLeadingInt = errors.New("units: bad [0-9]*") // never printed
+
+// leadingInt consumes the leading [0-9]* from s.
+func leadingInt(s string) (x int64, rem string, err error) {
+ i := 0
+ for ; i < len(s); i++ {
+ c := s[i]
+ if c < '0' || c > '9' {
+ break
+ }
+ if x >= (1<<63-10)/10 {
+ // overflow
+ return 0, "", errLeadingInt
+ }
+ x = x*10 + int64(c) - '0'
+ }
+ return x, s[i:], nil
+}
+
+func ParseUnit(s string, unitMap map[string]float64) (int64, error) {
+ // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
+ orig := s
+ f := float64(0)
+ neg := false
+
+ // Consume [-+]?
+ if s != "" {
+ c := s[0]
+ if c == '-' || c == '+' {
+ neg = c == '-'
+ s = s[1:]
+ }
+ }
+ // Special case: if all that is left is "0", this is zero.
+ if s == "0" {
+ return 0, nil
+ }
+ if s == "" {
+ return 0, errors.New("units: invalid " + orig)
+ }
+ for s != "" {
+ g := float64(0) // this element of the sequence
+
+ var x int64
+ var err error
+
+ // The next character must be [0-9.]
+ if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
+ return 0, errors.New("units: invalid " + orig)
+ }
+ // Consume [0-9]*
+ pl := len(s)
+ x, s, err = leadingInt(s)
+ if err != nil {
+ return 0, errors.New("units: invalid " + orig)
+ }
+ g = float64(x)
+ pre := pl != len(s) // whether we consumed anything before a period
+
+ // Consume (\.[0-9]*)?
+ post := false
+ if s != "" && s[0] == '.' {
+ s = s[1:]
+ pl := len(s)
+ x, s, err = leadingInt(s)
+ if err != nil {
+ return 0, errors.New("units: invalid " + orig)
+ }
+ scale := 1.0
+ for n := pl - len(s); n > 0; n-- {
+ scale *= 10
+ }
+ g += float64(x) / scale
+ post = pl != len(s)
+ }
+ if !pre && !post {
+ // no digits (e.g. ".s" or "-.s")
+ return 0, errors.New("units: invalid " + orig)
+ }
+
+ // Consume unit.
+ i := 0
+ for ; i < len(s); i++ {
+ c := s[i]
+ if c == '.' || ('0' <= c && c <= '9') {
+ break
+ }
+ }
+ u := s[:i]
+ s = s[i:]
+ unit, ok := unitMap[u]
+ if !ok {
+ return 0, errors.New("units: unknown unit " + u + " in " + orig)
+ }
+
+ f += g * unit
+ }
+
+ if neg {
+ f = -f
+ }
+ if f < float64(-1<<63) || f > float64(1<<63-1) {
+ return 0, errors.New("units: overflow parsing unit")
+ }
+ return int64(f), nil
+}
--- /dev/null
+Copyright (C) 2014 Alec Thomas
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+# Kingpin - A Go (golang) command line and flag parser
+[![](https://godoc.org/github.com/alecthomas/kingpin?status.svg)](http://godoc.org/github.com/alecthomas/kingpin) [![Build Status](https://travis-ci.org/alecthomas/kingpin.svg?branch=master)](https://travis-ci.org/alecthomas/kingpin) [![Gitter chat](https://badges.gitter.im/alecthomas.png)](https://gitter.im/alecthomas/Lobby)
+
+
+
+<!-- MarkdownTOC -->
+
+- [Overview](#overview)
+- [Features](#features)
+- [User-visible changes between v1 and v2](#user-visible-changes-between-v1-and-v2)
+ - [Flags can be used at any point after their definition.](#flags-can-be-used-at-any-point-after-their-definition)
+ - [Short flags can be combined with their parameters](#short-flags-can-be-combined-with-their-parameters)
+- [API changes between v1 and v2](#api-changes-between-v1-and-v2)
+- [Versions](#versions)
+ - [V2 is the current stable version](#v2-is-the-current-stable-version)
+ - [V1 is the OLD stable version](#v1-is-the-old-stable-version)
+- [Change History](#change-history)
+- [Examples](#examples)
+ - [Simple Example](#simple-example)
+ - [Complex Example](#complex-example)
+- [Reference Documentation](#reference-documentation)
+ - [Displaying errors and usage information](#displaying-errors-and-usage-information)
+ - [Sub-commands](#sub-commands)
+ - [Custom Parsers](#custom-parsers)
+ - [Repeatable flags](#repeatable-flags)
+ - [Boolean Values](#boolean-values)
+ - [Default Values](#default-values)
+ - [Place-holders in Help](#place-holders-in-help)
+ - [Consuming all remaining arguments](#consuming-all-remaining-arguments)
+ - [Bash/ZSH Shell Completion](#bashzsh-shell-completion)
+ - [Supporting -h for help](#supporting--h-for-help)
+ - [Custom help](#custom-help)
+
+<!-- /MarkdownTOC -->
+
+## Overview
+
+Kingpin is a [fluent-style](http://en.wikipedia.org/wiki/Fluent_interface),
+type-safe command-line parser. It supports flags, nested commands, and
+positional arguments.
+
+Install it with:
+
+ $ go get gopkg.in/alecthomas/kingpin.v2
+
+It looks like this:
+
+```go
+var (
+ verbose = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
+ name = kingpin.Arg("name", "Name of user.").Required().String()
+)
+
+func main() {
+ kingpin.Parse()
+ fmt.Printf("%v, %s\n", *verbose, *name)
+}
+```
+
+More [examples](https://github.com/alecthomas/kingpin/tree/master/_examples) are available.
+
+Second to parsing, providing the user with useful help is probably the most
+important thing a command-line parser does. Kingpin tries to provide detailed
+contextual help if `--help` is encountered at any point in the command line
+(excluding after `--`).
+
+## Features
+
+- Help output that isn't as ugly as sin.
+- Fully [customisable help](#custom-help), via Go templates.
+- Parsed, type-safe flags (`kingpin.Flag("f", "help").Int()`)
+- Parsed, type-safe positional arguments (`kingpin.Arg("a", "help").Int()`).
+- Parsed, type-safe, arbitrarily deep commands (`kingpin.Command("c", "help")`).
+- Support for required flags and required positional arguments (`kingpin.Flag("f", "").Required().Int()`).
+- Support for arbitrarily nested default commands (`command.Default()`).
+- Callbacks per command, flag and argument (`kingpin.Command("c", "").Action(myAction)`).
+- POSIX-style short flag combining (`-a -b` -> `-ab`).
+- Short-flag+parameter combining (`-a parm` -> `-aparm`).
+- Read command-line from files (`@<file>`).
+- Automatically generate man pages (`--help-man`).
+
+## User-visible changes between v1 and v2
+
+### Flags can be used at any point after their definition.
+
+Flags can be specified at any point after their definition, not just
+*immediately after their associated command*. From the chat example below, the
+following used to be required:
+
+```
+$ chat --server=chat.server.com:8080 post --image=~/Downloads/owls.jpg pics
+```
+
+But the following will now work:
+
+```
+$ chat post --server=chat.server.com:8080 --image=~/Downloads/owls.jpg pics
+```
+
+### Short flags can be combined with their parameters
+
+Previously, if a short flag was used, any argument to that flag would have to
+be separated by a space. That is no longer the case.
+
+## API changes between v1 and v2
+
+- `ParseWithFileExpansion()` is gone. The new parser directly supports expanding `@<file>`.
+- Added `FatalUsage()` and `FatalUsageContext()` for displaying an error + usage and terminating.
+- `Dispatch()` renamed to `Action()`.
+- Added `ParseContext()` for parsing a command line into its intermediate context form without executing.
+- Added `Terminate()` function to override the termination function.
+- Added `UsageForContextWithTemplate()` for printing usage via a custom template.
+- Added `UsageTemplate()` for overriding the default template to use. Two templates are included:
+ 1. `DefaultUsageTemplate` - default template.
+ 2. `CompactUsageTemplate` - compact command template for larger applications.
+
+## Versions
+
+Kingpin uses [gopkg.in](https://gopkg.in/alecthomas/kingpin) for versioning.
+
+The current stable version is [gopkg.in/alecthomas/kingpin.v2](https://gopkg.in/alecthomas/kingpin.v2). The previous version, [gopkg.in/alecthomas/kingpin.v1](https://gopkg.in/alecthomas/kingpin.v1), is deprecated and in maintenance mode.
+
+### [V2](https://gopkg.in/alecthomas/kingpin.v2) is the current stable version
+
+Installation:
+
+```sh
+$ go get gopkg.in/alecthomas/kingpin.v2
+```
+
+### [V1](https://gopkg.in/alecthomas/kingpin.v1) is the OLD stable version
+
+Installation:
+
+```sh
+$ go get gopkg.in/alecthomas/kingpin.v1
+```
+
+## Change History
+
+- *2015-09-19* -- Stable v2.1.0 release.
+ - Added `command.Default()` to specify a default command to use if no other
+ command matches. This allows for convenient user shortcuts.
+ - Exposed `HelpFlag` and `VersionFlag` for further customisation.
+ - `Action()` and `PreAction()` added and both now support an arbitrary
+ number of callbacks.
+ - `kingpin.SeparateOptionalFlagsUsageTemplate`.
+ - `--help-long` and `--help-man` (hidden by default) flags.
+ - Flags are "interspersed" by default, but can be disabled with `app.Interspersed(false)`.
+ - Added flags for all simple builtin types (int8, uint16, etc.) and slice variants.
+ - Use `app.Writer(os.Writer)` to specify the default writer for all output functions.
+ - Dropped `os.Writer` prefix from all printf-like functions.
+
+- *2015-05-22* -- Stable v2.0.0 release.
+ - Initial stable release of v2.0.0.
+ - Fully supports interspersed flags, commands and arguments.
+ - Flags can be present at any point after their logical definition.
+ - Application.Parse() terminates if commands are present and a command is not parsed.
+ - Dispatch() -> Action().
+ - Actions are dispatched after all values are populated.
+ - Override termination function (defaults to os.Exit).
+ - Override output stream (defaults to os.Stderr).
+ - Templatised usage help, with default and compact templates.
+ - Make error/usage functions more consistent.
+ - Support argument expansion from files by default (with @<file>).
+ - Fully public data model is available via .Model().
+ - Parser has been completely refactored.
+ - Parsing and execution has been split into distinct stages.
+ - Use `go generate` to generate repeated flags.
+ - Support combined short-flag+argument: -fARG.
+
+- *2015-01-23* -- Stable v1.3.4 release.
+ - Support "--" for separating flags from positional arguments.
+ - Support loading flags from files (ParseWithFileExpansion()). Use @FILE as an argument.
+ - Add post-app and post-cmd validation hooks. This allows arbitrary validation to be added.
+ - A bunch of improvements to help usage and formatting.
+ - Support arbitrarily nested sub-commands.
+
+- *2014-07-08* -- Stable v1.2.0 release.
+ - Pass any value through to `Strings()` when final argument.
+ Allows for values that look like flags to be processed.
+ - Allow `--help` to be used with commands.
+ - Support `Hidden()` flags.
+ - Parser for [units.Base2Bytes](https://github.com/alecthomas/units)
+ type. Allows for flags like `--ram=512MB` or `--ram=1GB`.
+ - Add an `Enum()` value, allowing only one of a set of values
+ to be selected. eg. `Flag(...).Enum("debug", "info", "warning")`.
+
+- *2014-06-27* -- Stable v1.1.0 release.
+ - Bug fixes.
+ - Always return an error (rather than panicing) when misconfigured.
+ - `OpenFile(flag, perm)` value type added, for finer control over opening files.
+ - Significantly improved usage formatting.
+
+- *2014-06-19* -- Stable v1.0.0 release.
+ - Support [cumulative positional](#consuming-all-remaining-arguments) arguments.
+ - Return error rather than panic when there are fatal errors not caught by
+ the type system. eg. when a default value is invalid.
+ - Use gokpg.in.
+
+- *2014-06-10* -- Place-holder streamlining.
+ - Renamed `MetaVar` to `PlaceHolder`.
+ - Removed `MetaVarFromDefault`. Kingpin now uses [heuristics](#place-holders-in-help)
+ to determine what to display.
+
+## Examples
+
+### Simple Example
+
+Kingpin can be used for simple flag+arg applications like so:
+
+```
+$ ping --help
+usage: ping [<flags>] <ip> [<count>]
+
+Flags:
+ --debug Enable debug mode.
+ --help Show help.
+ -t, --timeout=5s Timeout waiting for ping.
+
+Args:
+ <ip> IP address to ping.
+ [<count>] Number of packets to send
+$ ping 1.2.3.4 5
+Would ping: 1.2.3.4 with timeout 5s and count 0
+```
+
+From the following source:
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "gopkg.in/alecthomas/kingpin.v2"
+)
+
+var (
+ debug = kingpin.Flag("debug", "Enable debug mode.").Bool()
+ timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration()
+ ip = kingpin.Arg("ip", "IP address to ping.").Required().IP()
+ count = kingpin.Arg("count", "Number of packets to send").Int()
+)
+
+func main() {
+ kingpin.Version("0.0.1")
+ kingpin.Parse()
+ fmt.Printf("Would ping: %s with timeout %s and count %d\n", *ip, *timeout, *count)
+}
+```
+
+### Complex Example
+
+Kingpin can also produce complex command-line applications with global flags,
+subcommands, and per-subcommand flags, like this:
+
+```
+$ chat --help
+usage: chat [<flags>] <command> [<flags>] [<args> ...]
+
+A command-line chat application.
+
+Flags:
+ --help Show help.
+ --debug Enable debug mode.
+ --server=127.0.0.1 Server address.
+
+Commands:
+ help [<command>]
+ Show help for a command.
+
+ register <nick> <name>
+ Register a new user.
+
+ post [<flags>] <channel> [<text>]
+ Post a message to a channel.
+
+$ chat help post
+usage: chat [<flags>] post [<flags>] <channel> [<text>]
+
+Post a message to a channel.
+
+Flags:
+ --image=IMAGE Image to post.
+
+Args:
+ <channel> Channel to post to.
+ [<text>] Text to post.
+
+$ chat post --image=~/Downloads/owls.jpg pics
+...
+```
+
+From this code:
+
+```go
+package main
+
+import (
+ "os"
+ "strings"
+ "gopkg.in/alecthomas/kingpin.v2"
+)
+
+var (
+ app = kingpin.New("chat", "A command-line chat application.")
+ debug = app.Flag("debug", "Enable debug mode.").Bool()
+ serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP()
+
+ register = app.Command("register", "Register a new user.")
+ registerNick = register.Arg("nick", "Nickname for user.").Required().String()
+ registerName = register.Arg("name", "Name of user.").Required().String()
+
+ post = app.Command("post", "Post a message to a channel.")
+ postImage = post.Flag("image", "Image to post.").File()
+ postChannel = post.Arg("channel", "Channel to post to.").Required().String()
+ postText = post.Arg("text", "Text to post.").Strings()
+)
+
+func main() {
+ switch kingpin.MustParse(app.Parse(os.Args[1:])) {
+ // Register user
+ case register.FullCommand():
+ println(*registerNick)
+
+ // Post message
+ case post.FullCommand():
+ if *postImage != nil {
+ }
+ text := strings.Join(*postText, " ")
+ println("Post:", text)
+ }
+}
+```
+
+## Reference Documentation
+
+### Displaying errors and usage information
+
+Kingpin exports a set of functions to provide consistent errors and usage
+information to the user.
+
+Error messages look something like this:
+
+ <app>: error: <message>
+
+The functions on `Application` are:
+
+Function | Purpose
+---------|--------------
+`Errorf(format, args)` | Display a printf formatted error to the user.
+`Fatalf(format, args)` | As with Errorf, but also call the termination handler.
+`FatalUsage(format, args)` | As with Fatalf, but also print contextual usage information.
+`FatalUsageContext(context, format, args)` | As with Fatalf, but also print contextual usage information from a `ParseContext`.
+`FatalIfError(err, format, args)` | Conditionally print an error prefixed with format+args, then call the termination handler
+
+There are equivalent global functions in the kingpin namespace for the default
+`kingpin.CommandLine` instance.
+
+### Sub-commands
+
+Kingpin supports nested sub-commands, with separate flag and positional
+arguments per sub-command. Note that positional arguments may only occur after
+sub-commands.
+
+For example:
+
+```go
+var (
+ deleteCommand = kingpin.Command("delete", "Delete an object.")
+ deleteUserCommand = deleteCommand.Command("user", "Delete a user.")
+ deleteUserUIDFlag = deleteUserCommand.Flag("uid", "Delete user by UID rather than username.")
+ deleteUserUsername = deleteUserCommand.Arg("username", "Username to delete.")
+ deletePostCommand = deleteCommand.Command("post", "Delete a post.")
+)
+
+func main() {
+ switch kingpin.Parse() {
+ case "delete user":
+ case "delete post":
+ }
+}
+```
+
+### Custom Parsers
+
+Kingpin supports both flag and positional argument parsers for converting to
+Go types. For example, some included parsers are `Int()`, `Float()`,
+`Duration()` and `ExistingFile()` (see [parsers.go](./parsers.go) for a complete list of included parsers).
+
+Parsers conform to Go's [`flag.Value`](http://godoc.org/flag#Value)
+interface, so any existing implementations will work.
+
+For example, a parser for accumulating HTTP header values might look like this:
+
+```go
+type HTTPHeaderValue http.Header
+
+func (h *HTTPHeaderValue) Set(value string) error {
+ parts := strings.SplitN(value, ":", 2)
+ if len(parts) != 2 {
+ return fmt.Errorf("expected HEADER:VALUE got '%s'", value)
+ }
+ (*http.Header)(h).Add(parts[0], parts[1])
+ return nil
+}
+
+func (h *HTTPHeaderValue) String() string {
+ return ""
+}
+```
+
+As a convenience, I would recommend something like this:
+
+```go
+func HTTPHeader(s Settings) (target *http.Header) {
+ target = &http.Header{}
+ s.SetValue((*HTTPHeaderValue)(target))
+ return
+}
+```
+
+You would use it like so:
+
+```go
+headers = HTTPHeader(kingpin.Flag("header", "Add a HTTP header to the request.").Short('H'))
+```
+
+### Repeatable flags
+
+Depending on the `Value` they hold, some flags may be repeated. The
+`IsCumulative() bool` function on `Value` tells if it's safe to call `Set()`
+multiple times or if an error should be raised if several values are passed.
+
+The built-in `Value`s returning slices and maps, as well as `Counter` are
+examples of `Value`s that make a flag repeatable.
+
+### Boolean values
+
+Boolean values are uniquely managed by Kingpin. Each boolean flag will have a negative complement:
+`--<name>` and `--no-<name>`.
+
+### Default Values
+
+The default value is the zero value for a type. This can be overridden with
+the `Default(value...)` function on flags and arguments. This function accepts
+one or several strings, which are parsed by the value itself, so they *must*
+be compliant with the format expected.
+
+### Place-holders in Help
+
+The place-holder value for a flag is the value used in the help to describe
+the value of a non-boolean flag.
+
+The value provided to PlaceHolder() is used if provided, then the value
+provided by Default() if provided, then finally the capitalised flag name is
+used.
+
+Here are some examples of flags with various permutations:
+
+ --name=NAME // Flag(...).String()
+ --name="Harry" // Flag(...).Default("Harry").String()
+ --name=FULL-NAME // flag(...).PlaceHolder("FULL-NAME").Default("Harry").String()
+
+### Consuming all remaining arguments
+
+A common command-line idiom is to use all remaining arguments for some
+purpose. eg. The following command accepts an arbitrary number of
+IP addresses as positional arguments:
+
+ ./cmd ping 10.1.1.1 192.168.1.1
+
+Such arguments are similar to [repeatable flags](#repeatable-flags), but for
+arguments. Therefore they use the same `IsCumulative() bool` function on the
+underlying `Value`, so the built-in `Value`s for which the `Set()` function
+can be called several times will consume multiple arguments.
+
+To implement the above example with a custom `Value`, we might do something
+like this:
+
+```go
+type ipList []net.IP
+
+func (i *ipList) Set(value string) error {
+ if ip := net.ParseIP(value); ip == nil {
+ return fmt.Errorf("'%s' is not an IP address", value)
+ } else {
+ *i = append(*i, ip)
+ return nil
+ }
+}
+
+func (i *ipList) String() string {
+ return ""
+}
+
+func (i *ipList) IsCumulative() bool {
+ return true
+}
+
+func IPList(s Settings) (target *[]net.IP) {
+ target = new([]net.IP)
+ s.SetValue((*ipList)(target))
+ return
+}
+```
+
+And use it like so:
+
+```go
+ips := IPList(kingpin.Arg("ips", "IP addresses to ping."))
+```
+
+### Bash/ZSH Shell Completion
+
+By default, all flags and commands/subcommands generate completions
+internally.
+
+Out of the box, CLI tools using kingpin should be able to take advantage
+of completion hinting for flags and commands. By specifying
+`--completion-bash` as the first argument, your CLI tool will show
+possible subcommands. By ending your argv with `--`, hints for flags
+will be shown.
+
+To allow your end users to take advantage you must package a
+`/etc/bash_completion.d` script with your distribution (or the equivalent
+for your target platform/shell). An alternative is to instruct your end
+user to source a script from their `bash_profile` (or equivalent).
+
+Fortunately Kingpin makes it easy to generate or source a script for use
+with end users shells. `./yourtool --completion-script-bash` and
+`./yourtool --completion-script-zsh` will generate these scripts for you.
+
+**Installation by Package**
+
+For the best user experience, you should bundle your pre-created
+completion script with your CLI tool and install it inside
+`/etc/bash_completion.d` (or equivalent). A good suggestion is to add
+this as an automated step to your build pipeline, in the implementation
+is improved for bug fixed.
+
+**Installation by `bash_profile`**
+
+Alternatively, instruct your users to add an additional statement to
+their `bash_profile` (or equivalent):
+
+```
+eval "$(your-cli-tool --completion-script-bash)"
+```
+
+Or for ZSH
+
+```
+eval "$(your-cli-tool --completion-script-zsh)"
+```
+
+#### Additional API
+To provide more flexibility, a completion option API has been
+exposed for flags to allow user defined completion options, to extend
+completions further than just EnumVar/Enum.
+
+
+**Provide Static Options**
+
+When using an `Enum` or `EnumVar`, users are limited to only the options
+given. Maybe we wish to hint possible options to the user, but also
+allow them to provide their own custom option. `HintOptions` gives
+this functionality to flags.
+
+```
+app := kingpin.New("completion", "My application with bash completion.")
+app.Flag("port", "Provide a port to connect to").
+ Required().
+ HintOptions("80", "443", "8080").
+ IntVar(&c.port)
+```
+
+**Provide Dynamic Options**
+Consider the case that you needed to read a local database or a file to
+provide suggestions. You can dynamically generate the options
+
+```
+func listHosts(args []string) []string {
+ // Provide a dynamic list of hosts from a hosts file or otherwise
+ // for bash completion. In this example we simply return static slice.
+
+ // You could use this functionality to reach into a hosts file to provide
+ // completion for a list of known hosts.
+ return []string{"sshhost.example", "webhost.example", "ftphost.example"}
+}
+
+app := kingpin.New("completion", "My application with bash completion.")
+app.Flag("flag-1", "").HintAction(listHosts).String()
+```
+
+**EnumVar/Enum**
+When using `Enum` or `EnumVar`, any provided options will be automatically
+used for bash autocompletion. However, if you wish to provide a subset or
+different options, you can use `HintOptions` or `HintAction` which will override
+the default completion options for `Enum`/`EnumVar`.
+
+
+**Examples**
+You can see an in depth example of the completion API within
+`examples/completion/main.go`
+
+
+### Supporting -h for help
+
+`kingpin.CommandLine.HelpFlag.Short('h')`
+
+### Custom help
+
+Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)).
+
+You can specify the template to use with the [Application.UsageTemplate()](http://godoc.org/gopkg.in/alecthomas/kingpin.v2#Application.UsageTemplate) function.
+
+There are four included templates: `kingpin.DefaultUsageTemplate` is the default,
+`kingpin.CompactUsageTemplate` provides a more compact representation for more complex command-line structures,
+`kingpin.SeparateOptionalFlagsUsageTemplate` looks like the default template, but splits required
+and optional command flags into separate lists, and `kingpin.ManPageTemplate` is used to generate man pages.
+
+See the above templates for examples of usage, and the the function [UsageForContextWithTemplate()](https://github.com/alecthomas/kingpin/blob/master/usage.go#L198) method for details on the context.
+
+#### Default help template
+
+```
+$ go run ./examples/curl/curl.go --help
+usage: curl [<flags>] <command> [<args> ...]
+
+An example implementation of curl.
+
+Flags:
+ --help Show help.
+ -t, --timeout=5s Set connection timeout.
+ -H, --headers=HEADER=VALUE
+ Add HTTP headers to the request.
+
+Commands:
+ help [<command>...]
+ Show help.
+
+ get url <url>
+ Retrieve a URL.
+
+ get file <file>
+ Retrieve a file.
+
+ post [<flags>] <url>
+ POST a resource.
+```
+
+#### Compact help template
+
+```
+$ go run ./examples/curl/curl.go --help
+usage: curl [<flags>] <command> [<args> ...]
+
+An example implementation of curl.
+
+Flags:
+ --help Show help.
+ -t, --timeout=5s Set connection timeout.
+ -H, --headers=HEADER=VALUE
+ Add HTTP headers to the request.
+
+Commands:
+ help [<command>...]
+ get [<flags>]
+ url <url>
+ file <file>
+ post [<flags>] <url>
+```
--- /dev/null
+package kingpin
+
+// Action callback executed at various stages after all values are populated.
+// The application, commands, arguments and flags all have corresponding
+// actions.
+type Action func(*ParseContext) error
+
+type actionMixin struct {
+ actions []Action
+ preActions []Action
+}
+
+type actionApplier interface {
+ applyActions(*ParseContext) error
+ applyPreActions(*ParseContext) error
+}
+
+func (a *actionMixin) addAction(action Action) {
+ a.actions = append(a.actions, action)
+}
+
+func (a *actionMixin) addPreAction(action Action) {
+ a.preActions = append(a.preActions, action)
+}
+
+func (a *actionMixin) applyActions(context *ParseContext) error {
+ for _, action := range a.actions {
+ if err := action(context); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (a *actionMixin) applyPreActions(context *ParseContext) error {
+ for _, preAction := range a.preActions {
+ if err := preAction(context); err != nil {
+ return err
+ }
+ }
+ return nil
+}
--- /dev/null
+package kingpin
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+)
+
+var (
+ ErrCommandNotSpecified = fmt.Errorf("command not specified")
+)
+
+var (
+ envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]+`)
+)
+
+type ApplicationValidator func(*Application) error
+
+// An Application contains the definitions of flags, arguments and commands
+// for an application.
+type Application struct {
+ cmdMixin
+ initialized bool
+
+ Name string
+ Help string
+
+ author string
+ version string
+ errorWriter io.Writer // Destination for errors.
+ usageWriter io.Writer // Destination for usage
+ usageTemplate string
+ validator ApplicationValidator
+ terminate func(status int) // See Terminate()
+ noInterspersed bool // can flags be interspersed with args (or must they come first)
+ defaultEnvars bool
+ completion bool
+
+ // Help flag. Exposed for user customisation.
+ HelpFlag *FlagClause
+ // Help command. Exposed for user customisation. May be nil.
+ HelpCommand *CmdClause
+ // Version flag. Exposed for user customisation. May be nil.
+ VersionFlag *FlagClause
+}
+
+// New creates a new Kingpin application instance.
+func New(name, help string) *Application {
+ a := &Application{
+ Name: name,
+ Help: help,
+ errorWriter: os.Stderr, // Left for backwards compatibility purposes.
+ usageWriter: os.Stderr,
+ usageTemplate: DefaultUsageTemplate,
+ terminate: os.Exit,
+ }
+ a.flagGroup = newFlagGroup()
+ a.argGroup = newArgGroup()
+ a.cmdGroup = newCmdGroup(a)
+ a.HelpFlag = a.Flag("help", "Show context-sensitive help (also try --help-long and --help-man).")
+ a.HelpFlag.Bool()
+ a.Flag("help-long", "Generate long help.").Hidden().PreAction(a.generateLongHelp).Bool()
+ a.Flag("help-man", "Generate a man page.").Hidden().PreAction(a.generateManPage).Bool()
+ a.Flag("completion-bash", "Output possible completions for the given args.").Hidden().BoolVar(&a.completion)
+ a.Flag("completion-script-bash", "Generate completion script for bash.").Hidden().PreAction(a.generateBashCompletionScript).Bool()
+ a.Flag("completion-script-zsh", "Generate completion script for ZSH.").Hidden().PreAction(a.generateZSHCompletionScript).Bool()
+
+ return a
+}
+
+func (a *Application) generateLongHelp(c *ParseContext) error {
+ a.Writer(os.Stdout)
+ if err := a.UsageForContextWithTemplate(c, 2, LongHelpTemplate); err != nil {
+ return err
+ }
+ a.terminate(0)
+ return nil
+}
+
+func (a *Application) generateManPage(c *ParseContext) error {
+ a.Writer(os.Stdout)
+ if err := a.UsageForContextWithTemplate(c, 2, ManPageTemplate); err != nil {
+ return err
+ }
+ a.terminate(0)
+ return nil
+}
+
+func (a *Application) generateBashCompletionScript(c *ParseContext) error {
+ a.Writer(os.Stdout)
+ if err := a.UsageForContextWithTemplate(c, 2, BashCompletionTemplate); err != nil {
+ return err
+ }
+ a.terminate(0)
+ return nil
+}
+
+func (a *Application) generateZSHCompletionScript(c *ParseContext) error {
+ a.Writer(os.Stdout)
+ if err := a.UsageForContextWithTemplate(c, 2, ZshCompletionTemplate); err != nil {
+ return err
+ }
+ a.terminate(0)
+ return nil
+}
+
+// DefaultEnvars configures all flags (that do not already have an associated
+// envar) to use a default environment variable in the form "<app>_<flag>".
+//
+// For example, if the application is named "foo" and a flag is named "bar-
+// waz" the environment variable: "FOO_BAR_WAZ".
+func (a *Application) DefaultEnvars() *Application {
+ a.defaultEnvars = true
+ return a
+}
+
+// Terminate specifies the termination handler. Defaults to os.Exit(status).
+// If nil is passed, a no-op function will be used.
+func (a *Application) Terminate(terminate func(int)) *Application {
+ if terminate == nil {
+ terminate = func(int) {}
+ }
+ a.terminate = terminate
+ return a
+}
+
+// Writer specifies the writer to use for usage and errors. Defaults to os.Stderr.
+// DEPRECATED: See ErrorWriter and UsageWriter.
+func (a *Application) Writer(w io.Writer) *Application {
+ a.errorWriter = w
+ a.usageWriter = w
+ return a
+}
+
+// ErrorWriter sets the io.Writer to use for errors.
+func (a *Application) ErrorWriter(w io.Writer) *Application {
+ a.errorWriter = w
+ return a
+}
+
+// UsageWriter sets the io.Writer to use for errors.
+func (a *Application) UsageWriter(w io.Writer) *Application {
+ a.usageWriter = w
+ return a
+}
+
+// UsageTemplate specifies the text template to use when displaying usage
+// information. The default is UsageTemplate.
+func (a *Application) UsageTemplate(template string) *Application {
+ a.usageTemplate = template
+ return a
+}
+
+// Validate sets a validation function to run when parsing.
+func (a *Application) Validate(validator ApplicationValidator) *Application {
+ a.validator = validator
+ return a
+}
+
+// ParseContext parses the given command line and returns the fully populated
+// ParseContext.
+func (a *Application) ParseContext(args []string) (*ParseContext, error) {
+ return a.parseContext(false, args)
+}
+
+func (a *Application) parseContext(ignoreDefault bool, args []string) (*ParseContext, error) {
+ if err := a.init(); err != nil {
+ return nil, err
+ }
+ context := tokenize(args, ignoreDefault)
+ err := parse(context, a)
+ return context, err
+}
+
+// Parse parses command-line arguments. It returns the selected command and an
+// error. The selected command will be a space separated subcommand, if
+// subcommands have been configured.
+//
+// This will populate all flag and argument values, call all callbacks, and so
+// on.
+func (a *Application) Parse(args []string) (command string, err error) {
+
+ context, parseErr := a.ParseContext(args)
+ selected := []string{}
+ var setValuesErr error
+
+ if context == nil {
+ // Since we do not throw error immediately, there could be a case
+ // where a context returns nil. Protect against that.
+ return "", parseErr
+ }
+
+ if err = a.setDefaults(context); err != nil {
+ return "", err
+ }
+
+ selected, setValuesErr = a.setValues(context)
+
+ if err = a.applyPreActions(context, !a.completion); err != nil {
+ return "", err
+ }
+
+ if a.completion {
+ a.generateBashCompletion(context)
+ a.terminate(0)
+ } else {
+ if parseErr != nil {
+ return "", parseErr
+ }
+
+ a.maybeHelp(context)
+ if !context.EOL() {
+ return "", fmt.Errorf("unexpected argument '%s'", context.Peek())
+ }
+
+ if setValuesErr != nil {
+ return "", setValuesErr
+ }
+
+ command, err = a.execute(context, selected)
+ if err == ErrCommandNotSpecified {
+ a.writeUsage(context, nil)
+ }
+ }
+ return command, err
+}
+
+func (a *Application) writeUsage(context *ParseContext, err error) {
+ if err != nil {
+ a.Errorf("%s", err)
+ }
+ if err := a.UsageForContext(context); err != nil {
+ panic(err)
+ }
+ if err != nil {
+ a.terminate(1)
+ } else {
+ a.terminate(0)
+ }
+}
+
+func (a *Application) maybeHelp(context *ParseContext) {
+ for _, element := range context.Elements {
+ if flag, ok := element.Clause.(*FlagClause); ok && flag == a.HelpFlag {
+ // Re-parse the command-line ignoring defaults, so that help works correctly.
+ context, _ = a.parseContext(true, context.rawArgs)
+ a.writeUsage(context, nil)
+ }
+ }
+}
+
+// Version adds a --version flag for displaying the application version.
+func (a *Application) Version(version string) *Application {
+ a.version = version
+ a.VersionFlag = a.Flag("version", "Show application version.").PreAction(func(*ParseContext) error {
+ fmt.Fprintln(a.usageWriter, version)
+ a.terminate(0)
+ return nil
+ })
+ a.VersionFlag.Bool()
+ return a
+}
+
+// Author sets the author output by some help templates.
+func (a *Application) Author(author string) *Application {
+ a.author = author
+ return a
+}
+
+// Action callback to call when all values are populated and parsing is
+// complete, but before any command, flag or argument actions.
+//
+// All Action() callbacks are called in the order they are encountered on the
+// command line.
+func (a *Application) Action(action Action) *Application {
+ a.addAction(action)
+ return a
+}
+
+// Action called after parsing completes but before validation and execution.
+func (a *Application) PreAction(action Action) *Application {
+ a.addPreAction(action)
+ return a
+}
+
+// Command adds a new top-level command.
+func (a *Application) Command(name, help string) *CmdClause {
+ return a.addCommand(name, help)
+}
+
+// Interspersed control if flags can be interspersed with positional arguments
+//
+// true (the default) means that they can, false means that all the flags must appear before the first positional arguments.
+func (a *Application) Interspersed(interspersed bool) *Application {
+ a.noInterspersed = !interspersed
+ return a
+}
+
+func (a *Application) defaultEnvarPrefix() string {
+ if a.defaultEnvars {
+ return a.Name
+ }
+ return ""
+}
+
+func (a *Application) init() error {
+ if a.initialized {
+ return nil
+ }
+ if a.cmdGroup.have() && a.argGroup.have() {
+ return fmt.Errorf("can't mix top-level Arg()s with Command()s")
+ }
+
+ // If we have subcommands, add a help command at the top-level.
+ if a.cmdGroup.have() {
+ var command []string
+ a.HelpCommand = a.Command("help", "Show help.").PreAction(func(context *ParseContext) error {
+ a.Usage(command)
+ a.terminate(0)
+ return nil
+ })
+ a.HelpCommand.Arg("command", "Show help on command.").StringsVar(&command)
+ // Make help first command.
+ l := len(a.commandOrder)
+ a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...)
+ }
+
+ if err := a.flagGroup.init(a.defaultEnvarPrefix()); err != nil {
+ return err
+ }
+ if err := a.cmdGroup.init(); err != nil {
+ return err
+ }
+ if err := a.argGroup.init(); err != nil {
+ return err
+ }
+ for _, cmd := range a.commands {
+ if err := cmd.init(); err != nil {
+ return err
+ }
+ }
+ flagGroups := []*flagGroup{a.flagGroup}
+ for _, cmd := range a.commandOrder {
+ if err := checkDuplicateFlags(cmd, flagGroups); err != nil {
+ return err
+ }
+ }
+ a.initialized = true
+ return nil
+}
+
+// Recursively check commands for duplicate flags.
+func checkDuplicateFlags(current *CmdClause, flagGroups []*flagGroup) error {
+ // Check for duplicates.
+ for _, flags := range flagGroups {
+ for _, flag := range current.flagOrder {
+ if flag.shorthand != 0 {
+ if _, ok := flags.short[string(flag.shorthand)]; ok {
+ return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
+ }
+ }
+ if _, ok := flags.long[flag.name]; ok {
+ return fmt.Errorf("duplicate long flag --%s", flag.name)
+ }
+ }
+ }
+ flagGroups = append(flagGroups, current.flagGroup)
+ // Check subcommands.
+ for _, subcmd := range current.commandOrder {
+ if err := checkDuplicateFlags(subcmd, flagGroups); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (a *Application) execute(context *ParseContext, selected []string) (string, error) {
+ var err error
+
+ if err = a.validateRequired(context); err != nil {
+ return "", err
+ }
+
+ if err = a.applyValidators(context); err != nil {
+ return "", err
+ }
+
+ if err = a.applyActions(context); err != nil {
+ return "", err
+ }
+
+ command := strings.Join(selected, " ")
+ if command == "" && a.cmdGroup.have() {
+ return "", ErrCommandNotSpecified
+ }
+ return command, err
+}
+
+func (a *Application) setDefaults(context *ParseContext) error {
+ flagElements := map[string]*ParseElement{}
+ for _, element := range context.Elements {
+ if flag, ok := element.Clause.(*FlagClause); ok {
+ flagElements[flag.name] = element
+ }
+ }
+
+ argElements := map[string]*ParseElement{}
+ for _, element := range context.Elements {
+ if arg, ok := element.Clause.(*ArgClause); ok {
+ argElements[arg.name] = element
+ }
+ }
+
+ // Check required flags and set defaults.
+ for _, flag := range context.flags.long {
+ if flagElements[flag.name] == nil {
+ if err := flag.setDefault(); err != nil {
+ return err
+ }
+ }
+ }
+
+ for _, arg := range context.arguments.args {
+ if argElements[arg.name] == nil {
+ if err := arg.setDefault(); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func (a *Application) validateRequired(context *ParseContext) error {
+ flagElements := map[string]*ParseElement{}
+ for _, element := range context.Elements {
+ if flag, ok := element.Clause.(*FlagClause); ok {
+ flagElements[flag.name] = element
+ }
+ }
+
+ argElements := map[string]*ParseElement{}
+ for _, element := range context.Elements {
+ if arg, ok := element.Clause.(*ArgClause); ok {
+ argElements[arg.name] = element
+ }
+ }
+
+ // Check required flags and set defaults.
+ for _, flag := range context.flags.long {
+ if flagElements[flag.name] == nil {
+ // Check required flags were provided.
+ if flag.needsValue() {
+ return fmt.Errorf("required flag --%s not provided", flag.name)
+ }
+ }
+ }
+
+ for _, arg := range context.arguments.args {
+ if argElements[arg.name] == nil {
+ if arg.needsValue() {
+ return fmt.Errorf("required argument '%s' not provided", arg.name)
+ }
+ }
+ }
+ return nil
+}
+
+func (a *Application) setValues(context *ParseContext) (selected []string, err error) {
+ // Set all arg and flag values.
+ var (
+ lastCmd *CmdClause
+ flagSet = map[string]struct{}{}
+ )
+ for _, element := range context.Elements {
+ switch clause := element.Clause.(type) {
+ case *FlagClause:
+ if _, ok := flagSet[clause.name]; ok {
+ if v, ok := clause.value.(repeatableFlag); !ok || !v.IsCumulative() {
+ return nil, fmt.Errorf("flag '%s' cannot be repeated", clause.name)
+ }
+ }
+ if err = clause.value.Set(*element.Value); err != nil {
+ return
+ }
+ flagSet[clause.name] = struct{}{}
+
+ case *ArgClause:
+ if err = clause.value.Set(*element.Value); err != nil {
+ return
+ }
+
+ case *CmdClause:
+ if clause.validator != nil {
+ if err = clause.validator(clause); err != nil {
+ return
+ }
+ }
+ selected = append(selected, clause.name)
+ lastCmd = clause
+ }
+ }
+
+ if lastCmd != nil && len(lastCmd.commands) > 0 {
+ return nil, fmt.Errorf("must select a subcommand of '%s'", lastCmd.FullCommand())
+ }
+
+ return
+}
+
+func (a *Application) applyValidators(context *ParseContext) (err error) {
+ // Call command validation functions.
+ for _, element := range context.Elements {
+ if cmd, ok := element.Clause.(*CmdClause); ok && cmd.validator != nil {
+ if err = cmd.validator(cmd); err != nil {
+ return err
+ }
+ }
+ }
+
+ if a.validator != nil {
+ err = a.validator(a)
+ }
+ return err
+}
+
+func (a *Application) applyPreActions(context *ParseContext, dispatch bool) error {
+ if err := a.actionMixin.applyPreActions(context); err != nil {
+ return err
+ }
+ // Dispatch to actions.
+ if dispatch {
+ for _, element := range context.Elements {
+ if applier, ok := element.Clause.(actionApplier); ok {
+ if err := applier.applyPreActions(context); err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func (a *Application) applyActions(context *ParseContext) error {
+ if err := a.actionMixin.applyActions(context); err != nil {
+ return err
+ }
+ // Dispatch to actions.
+ for _, element := range context.Elements {
+ if applier, ok := element.Clause.(actionApplier); ok {
+ if err := applier.applyActions(context); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Errorf prints an error message to w in the format "<appname>: error: <message>".
+func (a *Application) Errorf(format string, args ...interface{}) {
+ fmt.Fprintf(a.errorWriter, a.Name+": error: "+format+"\n", args...)
+}
+
+// Fatalf writes a formatted error to w then terminates with exit status 1.
+func (a *Application) Fatalf(format string, args ...interface{}) {
+ a.Errorf(format, args...)
+ a.terminate(1)
+}
+
+// FatalUsage prints an error message followed by usage information, then
+// exits with a non-zero status.
+func (a *Application) FatalUsage(format string, args ...interface{}) {
+ a.Errorf(format, args...)
+ // Force usage to go to error output.
+ a.usageWriter = a.errorWriter
+ a.Usage([]string{})
+ a.terminate(1)
+}
+
+// FatalUsageContext writes a printf formatted error message to w, then usage
+// information for the given ParseContext, before exiting.
+func (a *Application) FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
+ a.Errorf(format, args...)
+ if err := a.UsageForContext(context); err != nil {
+ panic(err)
+ }
+ a.terminate(1)
+}
+
+// FatalIfError prints an error and exits if err is not nil. The error is printed
+// with the given formatted string, if any.
+func (a *Application) FatalIfError(err error, format string, args ...interface{}) {
+ if err != nil {
+ prefix := ""
+ if format != "" {
+ prefix = fmt.Sprintf(format, args...) + ": "
+ }
+ a.Errorf(prefix+"%s", err)
+ a.terminate(1)
+ }
+}
+
+func (a *Application) completionOptions(context *ParseContext) []string {
+ args := context.rawArgs
+
+ var (
+ currArg string
+ prevArg string
+ target cmdMixin
+ )
+
+ numArgs := len(args)
+ if numArgs > 1 {
+ args = args[1:]
+ currArg = args[len(args)-1]
+ }
+ if numArgs > 2 {
+ prevArg = args[len(args)-2]
+ }
+
+ target = a.cmdMixin
+ if context.SelectedCommand != nil {
+ // A subcommand was in use. We will use it as the target
+ target = context.SelectedCommand.cmdMixin
+ }
+
+ if (currArg != "" && strings.HasPrefix(currArg, "--")) || strings.HasPrefix(prevArg, "--") {
+ // Perform completion for A flag. The last/current argument started with "-"
+ var (
+ flagName string // The name of a flag if given (could be half complete)
+ flagValue string // The value assigned to a flag (if given) (could be half complete)
+ )
+
+ if strings.HasPrefix(prevArg, "--") && !strings.HasPrefix(currArg, "--") {
+ // Matches: ./myApp --flag value
+ // Wont Match: ./myApp --flag --
+ flagName = prevArg[2:] // Strip the "--"
+ flagValue = currArg
+ } else if strings.HasPrefix(currArg, "--") {
+ // Matches: ./myApp --flag --
+ // Matches: ./myApp --flag somevalue --
+ // Matches: ./myApp --
+ flagName = currArg[2:] // Strip the "--"
+ }
+
+ options, flagMatched, valueMatched := target.FlagCompletion(flagName, flagValue)
+ if valueMatched {
+ // Value Matched. Show cmdCompletions
+ return target.CmdCompletion(context)
+ }
+
+ // Add top level flags if we're not at the top level and no match was found.
+ if context.SelectedCommand != nil && !flagMatched {
+ topOptions, topFlagMatched, topValueMatched := a.FlagCompletion(flagName, flagValue)
+ if topValueMatched {
+ // Value Matched. Back to cmdCompletions
+ return target.CmdCompletion(context)
+ }
+
+ if topFlagMatched {
+ // Top level had a flag which matched the input. Return it's options.
+ options = topOptions
+ } else {
+ // Add top level flags
+ options = append(options, topOptions...)
+ }
+ }
+ return options
+ }
+
+ // Perform completion for sub commands and arguments.
+ return target.CmdCompletion(context)
+}
+
+func (a *Application) generateBashCompletion(context *ParseContext) {
+ options := a.completionOptions(context)
+ fmt.Printf("%s", strings.Join(options, "\n"))
+}
+
+func envarTransform(name string) string {
+ return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_"))
+}
--- /dev/null
+package kingpin
+
+import (
+ "fmt"
+)
+
+type argGroup struct {
+ args []*ArgClause
+}
+
+func newArgGroup() *argGroup {
+ return &argGroup{}
+}
+
+func (a *argGroup) have() bool {
+ return len(a.args) > 0
+}
+
+// GetArg gets an argument definition.
+//
+// This allows existing arguments to be modified after definition but before parsing. Useful for
+// modular applications.
+func (a *argGroup) GetArg(name string) *ArgClause {
+ for _, arg := range a.args {
+ if arg.name == name {
+ return arg
+ }
+ }
+ return nil
+}
+
+func (a *argGroup) Arg(name, help string) *ArgClause {
+ arg := newArg(name, help)
+ a.args = append(a.args, arg)
+ return arg
+}
+
+func (a *argGroup) init() error {
+ required := 0
+ seen := map[string]struct{}{}
+ previousArgMustBeLast := false
+ for i, arg := range a.args {
+ if previousArgMustBeLast {
+ return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name)
+ }
+ if arg.consumesRemainder() {
+ previousArgMustBeLast = true
+ }
+ if _, ok := seen[arg.name]; ok {
+ return fmt.Errorf("duplicate argument '%s'", arg.name)
+ }
+ seen[arg.name] = struct{}{}
+ if arg.required && required != i {
+ return fmt.Errorf("required arguments found after non-required")
+ }
+ if arg.required {
+ required++
+ }
+ if err := arg.init(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type ArgClause struct {
+ actionMixin
+ parserMixin
+ completionsMixin
+ envarMixin
+ name string
+ help string
+ defaultValues []string
+ required bool
+}
+
+func newArg(name, help string) *ArgClause {
+ a := &ArgClause{
+ name: name,
+ help: help,
+ }
+ return a
+}
+
+func (a *ArgClause) setDefault() error {
+ if a.HasEnvarValue() {
+ if v, ok := a.value.(remainderArg); !ok || !v.IsCumulative() {
+ // Use the value as-is
+ return a.value.Set(a.GetEnvarValue())
+ }
+ for _, value := range a.GetSplitEnvarValue() {
+ if err := a.value.Set(value); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ if len(a.defaultValues) > 0 {
+ for _, defaultValue := range a.defaultValues {
+ if err := a.value.Set(defaultValue); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ return nil
+}
+
+func (a *ArgClause) needsValue() bool {
+ haveDefault := len(a.defaultValues) > 0
+ return a.required && !(haveDefault || a.HasEnvarValue())
+}
+
+func (a *ArgClause) consumesRemainder() bool {
+ if r, ok := a.value.(remainderArg); ok {
+ return r.IsCumulative()
+ }
+ return false
+}
+
+// Required arguments must be input by the user. They can not have a Default() value provided.
+func (a *ArgClause) Required() *ArgClause {
+ a.required = true
+ return a
+}
+
+// Default values for this argument. They *must* be parseable by the value of the argument.
+func (a *ArgClause) Default(values ...string) *ArgClause {
+ a.defaultValues = values
+ return a
+}
+
+// Envar overrides the default value(s) for a flag from an environment variable,
+// if it is set. Several default values can be provided by using new lines to
+// separate them.
+func (a *ArgClause) Envar(name string) *ArgClause {
+ a.envar = name
+ a.noEnvar = false
+ return a
+}
+
+// NoEnvar forces environment variable defaults to be disabled for this flag.
+// Most useful in conjunction with app.DefaultEnvars().
+func (a *ArgClause) NoEnvar() *ArgClause {
+ a.envar = ""
+ a.noEnvar = true
+ return a
+}
+
+func (a *ArgClause) Action(action Action) *ArgClause {
+ a.addAction(action)
+ return a
+}
+
+func (a *ArgClause) PreAction(action Action) *ArgClause {
+ a.addPreAction(action)
+ return a
+}
+
+// HintAction registers a HintAction (function) for the arg to provide completions
+func (a *ArgClause) HintAction(action HintAction) *ArgClause {
+ a.addHintAction(action)
+ return a
+}
+
+// HintOptions registers any number of options for the flag to provide completions
+func (a *ArgClause) HintOptions(options ...string) *ArgClause {
+ a.addHintAction(func() []string {
+ return options
+ })
+ return a
+}
+
+func (a *ArgClause) init() error {
+ if a.required && len(a.defaultValues) > 0 {
+ return fmt.Errorf("required argument '%s' with unusable default value", a.name)
+ }
+ if a.value == nil {
+ return fmt.Errorf("no parser defined for arg '%s'", a.name)
+ }
+ return nil
+}
--- /dev/null
+package kingpin
+
+import (
+ "fmt"
+ "strings"
+)
+
+type cmdMixin struct {
+ *flagGroup
+ *argGroup
+ *cmdGroup
+ actionMixin
+}
+
+// CmdCompletion returns completion options for arguments, if that's where
+// parsing left off, or commands if there aren't any unsatisfied args.
+func (c *cmdMixin) CmdCompletion(context *ParseContext) []string {
+ var options []string
+
+ // Count args already satisfied - we won't complete those, and add any
+ // default commands' alternatives, since they weren't listed explicitly
+ // and the user may want to explicitly list something else.
+ argsSatisfied := 0
+ for _, el := range context.Elements {
+ switch clause := el.Clause.(type) {
+ case *ArgClause:
+ if el.Value != nil && *el.Value != "" {
+ argsSatisfied++
+ }
+ case *CmdClause:
+ options = append(options, clause.completionAlts...)
+ default:
+ }
+ }
+
+ if argsSatisfied < len(c.argGroup.args) {
+ // Since not all args have been satisfied, show options for the current one
+ options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...)
+ } else {
+ // If all args are satisfied, then go back to completing commands
+ for _, cmd := range c.cmdGroup.commandOrder {
+ if !cmd.hidden {
+ options = append(options, cmd.name)
+ }
+ }
+ }
+
+ return options
+}
+
+func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) {
+ // Check if flagName matches a known flag.
+ // If it does, show the options for the flag
+ // Otherwise, show all flags
+
+ options := []string{}
+
+ for _, flag := range c.flagGroup.flagOrder {
+ // Loop through each flag and determine if a match exists
+ if flag.name == flagName {
+ // User typed entire flag. Need to look for flag options.
+ options = flag.resolveCompletions()
+ if len(options) == 0 {
+ // No Options to Choose From, Assume Match.
+ return options, true, true
+ }
+
+ // Loop options to find if the user specified value matches
+ isPrefix := false
+ matched := false
+
+ for _, opt := range options {
+ if flagValue == opt {
+ matched = true
+ } else if strings.HasPrefix(opt, flagValue) {
+ isPrefix = true
+ }
+ }
+
+ // Matched Flag Directly
+ // Flag Value Not Prefixed, and Matched Directly
+ return options, true, !isPrefix && matched
+ }
+
+ if !flag.hidden {
+ options = append(options, "--"+flag.name)
+ }
+ }
+ // No Flag directly matched.
+ return options, false, false
+
+}
+
+type cmdGroup struct {
+ app *Application
+ parent *CmdClause
+ commands map[string]*CmdClause
+ commandOrder []*CmdClause
+}
+
+func (c *cmdGroup) defaultSubcommand() *CmdClause {
+ for _, cmd := range c.commandOrder {
+ if cmd.isDefault {
+ return cmd
+ }
+ }
+ return nil
+}
+
+func (c *cmdGroup) cmdNames() []string {
+ names := make([]string, 0, len(c.commandOrder))
+ for _, cmd := range c.commandOrder {
+ names = append(names, cmd.name)
+ }
+ return names
+}
+
+// GetArg gets a command definition.
+//
+// This allows existing commands to be modified after definition but before parsing. Useful for
+// modular applications.
+func (c *cmdGroup) GetCommand(name string) *CmdClause {
+ return c.commands[name]
+}
+
+func newCmdGroup(app *Application) *cmdGroup {
+ return &cmdGroup{
+ app: app,
+ commands: make(map[string]*CmdClause),
+ }
+}
+
+func (c *cmdGroup) flattenedCommands() (out []*CmdClause) {
+ for _, cmd := range c.commandOrder {
+ if len(cmd.commands) == 0 {
+ out = append(out, cmd)
+ }
+ out = append(out, cmd.flattenedCommands()...)
+ }
+ return
+}
+
+func (c *cmdGroup) addCommand(name, help string) *CmdClause {
+ cmd := newCommand(c.app, name, help)
+ c.commands[name] = cmd
+ c.commandOrder = append(c.commandOrder, cmd)
+ return cmd
+}
+
+func (c *cmdGroup) init() error {
+ seen := map[string]bool{}
+ if c.defaultSubcommand() != nil && !c.have() {
+ return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name)
+ }
+ defaults := []string{}
+ for _, cmd := range c.commandOrder {
+ if cmd.isDefault {
+ defaults = append(defaults, cmd.name)
+ }
+ if seen[cmd.name] {
+ return fmt.Errorf("duplicate command %q", cmd.name)
+ }
+ seen[cmd.name] = true
+ for _, alias := range cmd.aliases {
+ if seen[alias] {
+ return fmt.Errorf("alias duplicates existing command %q", alias)
+ }
+ c.commands[alias] = cmd
+ }
+ if err := cmd.init(); err != nil {
+ return err
+ }
+ }
+ if len(defaults) > 1 {
+ return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", "))
+ }
+ return nil
+}
+
+func (c *cmdGroup) have() bool {
+ return len(c.commands) > 0
+}
+
+type CmdClauseValidator func(*CmdClause) error
+
+// A CmdClause is a single top-level command. It encapsulates a set of flags
+// and either subcommands or positional arguments.
+type CmdClause struct {
+ cmdMixin
+ app *Application
+ name string
+ aliases []string
+ help string
+ isDefault bool
+ validator CmdClauseValidator
+ hidden bool
+ completionAlts []string
+}
+
+func newCommand(app *Application, name, help string) *CmdClause {
+ c := &CmdClause{
+ app: app,
+ name: name,
+ help: help,
+ }
+ c.flagGroup = newFlagGroup()
+ c.argGroup = newArgGroup()
+ c.cmdGroup = newCmdGroup(app)
+ return c
+}
+
+// Add an Alias for this command.
+func (c *CmdClause) Alias(name string) *CmdClause {
+ c.aliases = append(c.aliases, name)
+ return c
+}
+
+// Validate sets a validation function to run when parsing.
+func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause {
+ c.validator = validator
+ return c
+}
+
+func (c *CmdClause) FullCommand() string {
+ out := []string{c.name}
+ for p := c.parent; p != nil; p = p.parent {
+ out = append([]string{p.name}, out...)
+ }
+ return strings.Join(out, " ")
+}
+
+// Command adds a new sub-command.
+func (c *CmdClause) Command(name, help string) *CmdClause {
+ cmd := c.addCommand(name, help)
+ cmd.parent = c
+ return cmd
+}
+
+// Default makes this command the default if commands don't match.
+func (c *CmdClause) Default() *CmdClause {
+ c.isDefault = true
+ return c
+}
+
+func (c *CmdClause) Action(action Action) *CmdClause {
+ c.addAction(action)
+ return c
+}
+
+func (c *CmdClause) PreAction(action Action) *CmdClause {
+ c.addPreAction(action)
+ return c
+}
+
+func (c *CmdClause) init() error {
+ if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil {
+ return err
+ }
+ if c.argGroup.have() && c.cmdGroup.have() {
+ return fmt.Errorf("can't mix Arg()s with Command()s")
+ }
+ if err := c.argGroup.init(); err != nil {
+ return err
+ }
+ if err := c.cmdGroup.init(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *CmdClause) Hidden() *CmdClause {
+ c.hidden = true
+ return c
+}
--- /dev/null
+package kingpin
+
+// HintAction is a function type who is expected to return a slice of possible
+// command line arguments.
+type HintAction func() []string
+type completionsMixin struct {
+ hintActions []HintAction
+ builtinHintActions []HintAction
+}
+
+func (a *completionsMixin) addHintAction(action HintAction) {
+ a.hintActions = append(a.hintActions, action)
+}
+
+// Allow adding of HintActions which are added internally, ie, EnumVar
+func (a *completionsMixin) addHintActionBuiltin(action HintAction) {
+ a.builtinHintActions = append(a.builtinHintActions, action)
+}
+
+func (a *completionsMixin) resolveCompletions() []string {
+ var hints []string
+
+ options := a.builtinHintActions
+ if len(a.hintActions) > 0 {
+ // User specified their own hintActions. Use those instead.
+ options = a.hintActions
+ }
+
+ for _, hintAction := range options {
+ hints = append(hints, hintAction()...)
+ }
+ return hints
+}
--- /dev/null
+// Package kingpin provides command line interfaces like this:
+//
+// $ chat
+// usage: chat [<flags>] <command> [<flags>] [<args> ...]
+//
+// Flags:
+// --debug enable debug mode
+// --help Show help.
+// --server=127.0.0.1 server address
+//
+// Commands:
+// help <command>
+// Show help for a command.
+//
+// post [<flags>] <channel>
+// Post a message to a channel.
+//
+// register <nick> <name>
+// Register a new user.
+//
+// $ chat help post
+// usage: chat [<flags>] post [<flags>] <channel> [<text>]
+//
+// Post a message to a channel.
+//
+// Flags:
+// --image=IMAGE image to post
+//
+// Args:
+// <channel> channel to post to
+// [<text>] text to post
+// $ chat post --image=~/Downloads/owls.jpg pics
+//
+// From code like this:
+//
+// package main
+//
+// import "gopkg.in/alecthomas/kingpin.v2"
+//
+// var (
+// debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool()
+// serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP()
+//
+// register = kingpin.Command("register", "Register a new user.")
+// registerNick = register.Arg("nick", "nickname for user").Required().String()
+// registerName = register.Arg("name", "name of user").Required().String()
+//
+// post = kingpin.Command("post", "Post a message to a channel.")
+// postImage = post.Flag("image", "image to post").ExistingFile()
+// postChannel = post.Arg("channel", "channel to post to").Required().String()
+// postText = post.Arg("text", "text to post").String()
+// )
+//
+// func main() {
+// switch kingpin.Parse() {
+// // Register user
+// case "register":
+// println(*registerNick)
+//
+// // Post message
+// case "post":
+// if *postImage != nil {
+// }
+// if *postText != "" {
+// }
+// }
+// }
+package kingpin
--- /dev/null
+package kingpin
+
+import (
+ "os"
+ "regexp"
+)
+
+var (
+ envVarValuesSeparator = "\r?\n"
+ envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$")
+ envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator)
+)
+
+type envarMixin struct {
+ envar string
+ noEnvar bool
+}
+
+func (e *envarMixin) HasEnvarValue() bool {
+ return e.GetEnvarValue() != ""
+}
+
+func (e *envarMixin) GetEnvarValue() string {
+ if e.noEnvar || e.envar == "" {
+ return ""
+ }
+ return os.Getenv(e.envar)
+}
+
+func (e *envarMixin) GetSplitEnvarValue() []string {
+ values := make([]string, 0)
+
+ envarValue := e.GetEnvarValue()
+ if envarValue == "" {
+ return values
+ }
+
+ // Split by new line to extract multiple values, if any.
+ trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "")
+ for _, value := range envVarValuesSplitter.Split(trimmed, -1) {
+ values = append(values, value)
+ }
+
+ return values
+}
--- /dev/null
+package kingpin
+
+import (
+ "fmt"
+ "strings"
+)
+
+type flagGroup struct {
+ short map[string]*FlagClause
+ long map[string]*FlagClause
+ flagOrder []*FlagClause
+}
+
+func newFlagGroup() *flagGroup {
+ return &flagGroup{
+ short: map[string]*FlagClause{},
+ long: map[string]*FlagClause{},
+ }
+}
+
+// GetFlag gets a flag definition.
+//
+// This allows existing flags to be modified after definition but before parsing. Useful for
+// modular applications.
+func (f *flagGroup) GetFlag(name string) *FlagClause {
+ return f.long[name]
+}
+
+// Flag defines a new flag with the given long name and help.
+func (f *flagGroup) Flag(name, help string) *FlagClause {
+ flag := newFlag(name, help)
+ f.long[name] = flag
+ f.flagOrder = append(f.flagOrder, flag)
+ return flag
+}
+
+func (f *flagGroup) init(defaultEnvarPrefix string) error {
+ if err := f.checkDuplicates(); err != nil {
+ return err
+ }
+ for _, flag := range f.long {
+ if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" {
+ flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name)
+ }
+ if err := flag.init(); err != nil {
+ return err
+ }
+ if flag.shorthand != 0 {
+ f.short[string(flag.shorthand)] = flag
+ }
+ }
+ return nil
+}
+
+func (f *flagGroup) checkDuplicates() error {
+ seenShort := map[rune]bool{}
+ seenLong := map[string]bool{}
+ for _, flag := range f.flagOrder {
+ if flag.shorthand != 0 {
+ if _, ok := seenShort[flag.shorthand]; ok {
+ return fmt.Errorf("duplicate short flag -%c", flag.shorthand)
+ }
+ seenShort[flag.shorthand] = true
+ }
+ if _, ok := seenLong[flag.name]; ok {
+ return fmt.Errorf("duplicate long flag --%s", flag.name)
+ }
+ seenLong[flag.name] = true
+ }
+ return nil
+}
+
+func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) {
+ var token *Token
+
+loop:
+ for {
+ token = context.Peek()
+ switch token.Type {
+ case TokenEOL:
+ break loop
+
+ case TokenLong, TokenShort:
+ flagToken := token
+ defaultValue := ""
+ var flag *FlagClause
+ var ok bool
+ invert := false
+
+ name := token.Value
+ if token.Type == TokenLong {
+ flag, ok = f.long[name]
+ if !ok {
+ if strings.HasPrefix(name, "no-") {
+ name = name[3:]
+ invert = true
+ }
+ flag, ok = f.long[name]
+ }
+ if !ok {
+ return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
+ }
+ } else {
+ flag, ok = f.short[name]
+ if !ok {
+ return nil, fmt.Errorf("unknown short flag '%s'", flagToken)
+ }
+ }
+
+ context.Next()
+
+ fb, ok := flag.value.(boolFlag)
+ if ok && fb.IsBoolFlag() {
+ if invert {
+ defaultValue = "false"
+ } else {
+ defaultValue = "true"
+ }
+ } else {
+ if invert {
+ context.Push(token)
+ return nil, fmt.Errorf("unknown long flag '%s'", flagToken)
+ }
+ token = context.Peek()
+ if token.Type != TokenArg {
+ context.Push(token)
+ return nil, fmt.Errorf("expected argument for flag '%s'", flagToken)
+ }
+ context.Next()
+ defaultValue = token.Value
+ }
+
+ context.matchedFlag(flag, defaultValue)
+ return flag, nil
+
+ default:
+ break loop
+ }
+ }
+ return nil, nil
+}
+
+// FlagClause is a fluid interface used to build flags.
+type FlagClause struct {
+ parserMixin
+ actionMixin
+ completionsMixin
+ envarMixin
+ name string
+ shorthand rune
+ help string
+ defaultValues []string
+ placeholder string
+ hidden bool
+}
+
+func newFlag(name, help string) *FlagClause {
+ f := &FlagClause{
+ name: name,
+ help: help,
+ }
+ return f
+}
+
+func (f *FlagClause) setDefault() error {
+ if f.HasEnvarValue() {
+ if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() {
+ // Use the value as-is
+ return f.value.Set(f.GetEnvarValue())
+ } else {
+ for _, value := range f.GetSplitEnvarValue() {
+ if err := f.value.Set(value); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ }
+
+ if len(f.defaultValues) > 0 {
+ for _, defaultValue := range f.defaultValues {
+ if err := f.value.Set(defaultValue); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+
+ return nil
+}
+
+func (f *FlagClause) needsValue() bool {
+ haveDefault := len(f.defaultValues) > 0
+ return f.required && !(haveDefault || f.HasEnvarValue())
+}
+
+func (f *FlagClause) init() error {
+ if f.required && len(f.defaultValues) > 0 {
+ return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name)
+ }
+ if f.value == nil {
+ return fmt.Errorf("no type defined for --%s (eg. .String())", f.name)
+ }
+ if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 {
+ return fmt.Errorf("invalid default for '--%s', expecting single value", f.name)
+ }
+ return nil
+}
+
+// Dispatch to the given function after the flag is parsed and validated.
+func (f *FlagClause) Action(action Action) *FlagClause {
+ f.addAction(action)
+ return f
+}
+
+func (f *FlagClause) PreAction(action Action) *FlagClause {
+ f.addPreAction(action)
+ return f
+}
+
+// HintAction registers a HintAction (function) for the flag to provide completions
+func (a *FlagClause) HintAction(action HintAction) *FlagClause {
+ a.addHintAction(action)
+ return a
+}
+
+// HintOptions registers any number of options for the flag to provide completions
+func (a *FlagClause) HintOptions(options ...string) *FlagClause {
+ a.addHintAction(func() []string {
+ return options
+ })
+ return a
+}
+
+func (a *FlagClause) EnumVar(target *string, options ...string) {
+ a.parserMixin.EnumVar(target, options...)
+ a.addHintActionBuiltin(func() []string {
+ return options
+ })
+}
+
+func (a *FlagClause) Enum(options ...string) (target *string) {
+ a.addHintActionBuiltin(func() []string {
+ return options
+ })
+ return a.parserMixin.Enum(options...)
+}
+
+// Default values for this flag. They *must* be parseable by the value of the flag.
+func (f *FlagClause) Default(values ...string) *FlagClause {
+ f.defaultValues = values
+ return f
+}
+
+// DEPRECATED: Use Envar(name) instead.
+func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause {
+ return f.Envar(envar)
+}
+
+// Envar overrides the default value(s) for a flag from an environment variable,
+// if it is set. Several default values can be provided by using new lines to
+// separate them.
+func (f *FlagClause) Envar(name string) *FlagClause {
+ f.envar = name
+ f.noEnvar = false
+ return f
+}
+
+// NoEnvar forces environment variable defaults to be disabled for this flag.
+// Most useful in conjunction with app.DefaultEnvars().
+func (f *FlagClause) NoEnvar() *FlagClause {
+ f.envar = ""
+ f.noEnvar = true
+ return f
+}
+
+// PlaceHolder sets the place-holder string used for flag values in the help. The
+// default behaviour is to use the value provided by Default() if provided,
+// then fall back on the capitalized flag name.
+func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause {
+ f.placeholder = placeholder
+ return f
+}
+
+// Hidden hides a flag from usage but still allows it to be used.
+func (f *FlagClause) Hidden() *FlagClause {
+ f.hidden = true
+ return f
+}
+
+// Required makes the flag required. You can not provide a Default() value to a Required() flag.
+func (f *FlagClause) Required() *FlagClause {
+ f.required = true
+ return f
+}
+
+// Short sets the short flag name.
+func (f *FlagClause) Short(name rune) *FlagClause {
+ f.shorthand = name
+ return f
+}
+
+// Bool makes this flag a boolean flag.
+func (f *FlagClause) Bool() (target *bool) {
+ target = new(bool)
+ f.SetValue(newBoolValue(target))
+ return
+}
--- /dev/null
+package kingpin
+
+import (
+ "os"
+ "path/filepath"
+)
+
+var (
+ // CommandLine is the default Kingpin parser.
+ CommandLine = New(filepath.Base(os.Args[0]), "")
+ // Global help flag. Exposed for user customisation.
+ HelpFlag = CommandLine.HelpFlag
+ // Top-level help command. Exposed for user customisation. May be nil.
+ HelpCommand = CommandLine.HelpCommand
+ // Global version flag. Exposed for user customisation. May be nil.
+ VersionFlag = CommandLine.VersionFlag
+)
+
+// Command adds a new command to the default parser.
+func Command(name, help string) *CmdClause {
+ return CommandLine.Command(name, help)
+}
+
+// Flag adds a new flag to the default parser.
+func Flag(name, help string) *FlagClause {
+ return CommandLine.Flag(name, help)
+}
+
+// Arg adds a new argument to the top-level of the default parser.
+func Arg(name, help string) *ArgClause {
+ return CommandLine.Arg(name, help)
+}
+
+// Parse and return the selected command. Will call the termination handler if
+// an error is encountered.
+func Parse() string {
+ selected := MustParse(CommandLine.Parse(os.Args[1:]))
+ if selected == "" && CommandLine.cmdGroup.have() {
+ Usage()
+ CommandLine.terminate(0)
+ }
+ return selected
+}
+
+// Errorf prints an error message to stderr.
+func Errorf(format string, args ...interface{}) {
+ CommandLine.Errorf(format, args...)
+}
+
+// Fatalf prints an error message to stderr and exits.
+func Fatalf(format string, args ...interface{}) {
+ CommandLine.Fatalf(format, args...)
+}
+
+// FatalIfError prints an error and exits if err is not nil. The error is printed
+// with the given prefix.
+func FatalIfError(err error, format string, args ...interface{}) {
+ CommandLine.FatalIfError(err, format, args...)
+}
+
+// FatalUsage prints an error message followed by usage information, then
+// exits with a non-zero status.
+func FatalUsage(format string, args ...interface{}) {
+ CommandLine.FatalUsage(format, args...)
+}
+
+// FatalUsageContext writes a printf formatted error message to stderr, then
+// usage information for the given ParseContext, before exiting.
+func FatalUsageContext(context *ParseContext, format string, args ...interface{}) {
+ CommandLine.FatalUsageContext(context, format, args...)
+}
+
+// Usage prints usage to stderr.
+func Usage() {
+ CommandLine.Usage(os.Args[1:])
+}
+
+// Set global usage template to use (defaults to DefaultUsageTemplate).
+func UsageTemplate(template string) *Application {
+ return CommandLine.UsageTemplate(template)
+}
+
+// MustParse can be used with app.Parse(args) to exit with an error if parsing fails.
+func MustParse(command string, err error) string {
+ if err != nil {
+ Fatalf("%s, try --help", err)
+ }
+ return command
+}
+
+// Version adds a flag for displaying the application version number.
+func Version(version string) *Application {
+ return CommandLine.Version(version)
+}
--- /dev/null
+// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
+
+package kingpin
+
+import "io"
+
+func guessWidth(w io.Writer) int {
+ return 80
+}
--- /dev/null
+// +build !appengine,linux freebsd darwin dragonfly netbsd openbsd
+
+package kingpin
+
+import (
+ "io"
+ "os"
+ "strconv"
+ "syscall"
+ "unsafe"
+)
+
+func guessWidth(w io.Writer) int {
+ // check if COLUMNS env is set to comply with
+ // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html
+ colsStr := os.Getenv("COLUMNS")
+ if colsStr != "" {
+ if cols, err := strconv.Atoi(colsStr); err == nil {
+ return cols
+ }
+ }
+
+ if t, ok := w.(*os.File); ok {
+ fd := t.Fd()
+ var dimensions [4]uint16
+
+ if _, _, err := syscall.Syscall6(
+ syscall.SYS_IOCTL,
+ uintptr(fd),
+ uintptr(syscall.TIOCGWINSZ),
+ uintptr(unsafe.Pointer(&dimensions)),
+ 0, 0, 0,
+ ); err == 0 {
+ return int(dimensions[1])
+ }
+ }
+ return 80
+}
--- /dev/null
+package kingpin
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// Data model for Kingpin command-line structure.
+
+type FlagGroupModel struct {
+ Flags []*FlagModel
+}
+
+func (f *FlagGroupModel) FlagSummary() string {
+ out := []string{}
+ count := 0
+ for _, flag := range f.Flags {
+ if flag.Name != "help" {
+ count++
+ }
+ if flag.Required {
+ if flag.IsBoolFlag() {
+ out = append(out, fmt.Sprintf("--[no-]%s", flag.Name))
+ } else {
+ out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder()))
+ }
+ }
+ }
+ if count != len(out) {
+ out = append(out, "[<flags>]")
+ }
+ return strings.Join(out, " ")
+}
+
+type FlagModel struct {
+ Name string
+ Help string
+ Short rune
+ Default []string
+ Envar string
+ PlaceHolder string
+ Required bool
+ Hidden bool
+ Value Value
+}
+
+func (f *FlagModel) String() string {
+ return f.Value.String()
+}
+
+func (f *FlagModel) IsBoolFlag() bool {
+ if fl, ok := f.Value.(boolFlag); ok {
+ return fl.IsBoolFlag()
+ }
+ return false
+}
+
+func (f *FlagModel) FormatPlaceHolder() string {
+ if f.PlaceHolder != "" {
+ return f.PlaceHolder
+ }
+ if len(f.Default) > 0 {
+ ellipsis := ""
+ if len(f.Default) > 1 {
+ ellipsis = "..."
+ }
+ if _, ok := f.Value.(*stringValue); ok {
+ return strconv.Quote(f.Default[0]) + ellipsis
+ }
+ return f.Default[0] + ellipsis
+ }
+ return strings.ToUpper(f.Name)
+}
+
+type ArgGroupModel struct {
+ Args []*ArgModel
+}
+
+func (a *ArgGroupModel) ArgSummary() string {
+ depth := 0
+ out := []string{}
+ for _, arg := range a.Args {
+ h := "<" + arg.Name + ">"
+ if !arg.Required {
+ h = "[" + h
+ depth++
+ }
+ out = append(out, h)
+ }
+ out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth)
+ return strings.Join(out, " ")
+}
+
+type ArgModel struct {
+ Name string
+ Help string
+ Default []string
+ Envar string
+ Required bool
+ Value Value
+}
+
+func (a *ArgModel) String() string {
+ return a.Value.String()
+}
+
+type CmdGroupModel struct {
+ Commands []*CmdModel
+}
+
+func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) {
+ for _, cmd := range c.Commands {
+ if len(cmd.Commands) == 0 {
+ out = append(out, cmd)
+ }
+ out = append(out, cmd.FlattenedCommands()...)
+ }
+ return
+}
+
+type CmdModel struct {
+ Name string
+ Aliases []string
+ Help string
+ FullCommand string
+ Depth int
+ Hidden bool
+ Default bool
+ *FlagGroupModel
+ *ArgGroupModel
+ *CmdGroupModel
+}
+
+func (c *CmdModel) String() string {
+ return c.FullCommand
+}
+
+type ApplicationModel struct {
+ Name string
+ Help string
+ Version string
+ Author string
+ *ArgGroupModel
+ *CmdGroupModel
+ *FlagGroupModel
+}
+
+func (a *Application) Model() *ApplicationModel {
+ return &ApplicationModel{
+ Name: a.Name,
+ Help: a.Help,
+ Version: a.version,
+ Author: a.author,
+ FlagGroupModel: a.flagGroup.Model(),
+ ArgGroupModel: a.argGroup.Model(),
+ CmdGroupModel: a.cmdGroup.Model(),
+ }
+}
+
+func (a *argGroup) Model() *ArgGroupModel {
+ m := &ArgGroupModel{}
+ for _, arg := range a.args {
+ m.Args = append(m.Args, arg.Model())
+ }
+ return m
+}
+
+func (a *ArgClause) Model() *ArgModel {
+ return &ArgModel{
+ Name: a.name,
+ Help: a.help,
+ Default: a.defaultValues,
+ Envar: a.envar,
+ Required: a.required,
+ Value: a.value,
+ }
+}
+
+func (f *flagGroup) Model() *FlagGroupModel {
+ m := &FlagGroupModel{}
+ for _, fl := range f.flagOrder {
+ m.Flags = append(m.Flags, fl.Model())
+ }
+ return m
+}
+
+func (f *FlagClause) Model() *FlagModel {
+ return &FlagModel{
+ Name: f.name,
+ Help: f.help,
+ Short: rune(f.shorthand),
+ Default: f.defaultValues,
+ Envar: f.envar,
+ PlaceHolder: f.placeholder,
+ Required: f.required,
+ Hidden: f.hidden,
+ Value: f.value,
+ }
+}
+
+func (c *cmdGroup) Model() *CmdGroupModel {
+ m := &CmdGroupModel{}
+ for _, cm := range c.commandOrder {
+ m.Commands = append(m.Commands, cm.Model())
+ }
+ return m
+}
+
+func (c *CmdClause) Model() *CmdModel {
+ depth := 0
+ for i := c; i != nil; i = i.parent {
+ depth++
+ }
+ return &CmdModel{
+ Name: c.name,
+ Aliases: c.aliases,
+ Help: c.help,
+ Depth: depth,
+ Hidden: c.hidden,
+ Default: c.isDefault,
+ FullCommand: c.FullCommand(),
+ FlagGroupModel: c.flagGroup.Model(),
+ ArgGroupModel: c.argGroup.Model(),
+ CmdGroupModel: c.cmdGroup.Model(),
+ }
+}
--- /dev/null
+package kingpin
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+ "unicode/utf8"
+)
+
+type TokenType int
+
+// Token types.
+const (
+ TokenShort TokenType = iota
+ TokenLong
+ TokenArg
+ TokenError
+ TokenEOL
+)
+
+func (t TokenType) String() string {
+ switch t {
+ case TokenShort:
+ return "short flag"
+ case TokenLong:
+ return "long flag"
+ case TokenArg:
+ return "argument"
+ case TokenError:
+ return "error"
+ case TokenEOL:
+ return "<EOL>"
+ }
+ return "?"
+}
+
+var (
+ TokenEOLMarker = Token{-1, TokenEOL, ""}
+)
+
+type Token struct {
+ Index int
+ Type TokenType
+ Value string
+}
+
+func (t *Token) Equal(o *Token) bool {
+ return t.Index == o.Index
+}
+
+func (t *Token) IsFlag() bool {
+ return t.Type == TokenShort || t.Type == TokenLong
+}
+
+func (t *Token) IsEOF() bool {
+ return t.Type == TokenEOL
+}
+
+func (t *Token) String() string {
+ switch t.Type {
+ case TokenShort:
+ return "-" + t.Value
+ case TokenLong:
+ return "--" + t.Value
+ case TokenArg:
+ return t.Value
+ case TokenError:
+ return "error: " + t.Value
+ case TokenEOL:
+ return "<EOL>"
+ default:
+ panic("unhandled type")
+ }
+}
+
+// A union of possible elements in a parse stack.
+type ParseElement struct {
+ // Clause is either *CmdClause, *ArgClause or *FlagClause.
+ Clause interface{}
+ // Value is corresponding value for an ArgClause or FlagClause (if any).
+ Value *string
+}
+
+// ParseContext holds the current context of the parser. When passed to
+// Action() callbacks Elements will be fully populated with *FlagClause,
+// *ArgClause and *CmdClause values and their corresponding arguments (if
+// any).
+type ParseContext struct {
+ SelectedCommand *CmdClause
+ ignoreDefault bool
+ argsOnly bool
+ peek []*Token
+ argi int // Index of current command-line arg we're processing.
+ args []string
+ rawArgs []string
+ flags *flagGroup
+ arguments *argGroup
+ argumenti int // Cursor into arguments
+ // Flags, arguments and commands encountered and collected during parse.
+ Elements []*ParseElement
+}
+
+func (p *ParseContext) nextArg() *ArgClause {
+ if p.argumenti >= len(p.arguments.args) {
+ return nil
+ }
+ arg := p.arguments.args[p.argumenti]
+ if !arg.consumesRemainder() {
+ p.argumenti++
+ }
+ return arg
+}
+
+func (p *ParseContext) next() {
+ p.argi++
+ p.args = p.args[1:]
+}
+
+// HasTrailingArgs returns true if there are unparsed command-line arguments.
+// This can occur if the parser can not match remaining arguments.
+func (p *ParseContext) HasTrailingArgs() bool {
+ return len(p.args) > 0
+}
+
+func tokenize(args []string, ignoreDefault bool) *ParseContext {
+ return &ParseContext{
+ ignoreDefault: ignoreDefault,
+ args: args,
+ rawArgs: args,
+ flags: newFlagGroup(),
+ arguments: newArgGroup(),
+ }
+}
+
+func (p *ParseContext) mergeFlags(flags *flagGroup) {
+ for _, flag := range flags.flagOrder {
+ if flag.shorthand != 0 {
+ p.flags.short[string(flag.shorthand)] = flag
+ }
+ p.flags.long[flag.name] = flag
+ p.flags.flagOrder = append(p.flags.flagOrder, flag)
+ }
+}
+
+func (p *ParseContext) mergeArgs(args *argGroup) {
+ for _, arg := range args.args {
+ p.arguments.args = append(p.arguments.args, arg)
+ }
+}
+
+func (p *ParseContext) EOL() bool {
+ return p.Peek().Type == TokenEOL
+}
+
+// Next token in the parse context.
+func (p *ParseContext) Next() *Token {
+ if len(p.peek) > 0 {
+ return p.pop()
+ }
+
+ // End of tokens.
+ if len(p.args) == 0 {
+ return &Token{Index: p.argi, Type: TokenEOL}
+ }
+
+ arg := p.args[0]
+ p.next()
+
+ if p.argsOnly {
+ return &Token{p.argi, TokenArg, arg}
+ }
+
+ // All remaining args are passed directly.
+ if arg == "--" {
+ p.argsOnly = true
+ return p.Next()
+ }
+
+ if strings.HasPrefix(arg, "--") {
+ parts := strings.SplitN(arg[2:], "=", 2)
+ token := &Token{p.argi, TokenLong, parts[0]}
+ if len(parts) == 2 {
+ p.Push(&Token{p.argi, TokenArg, parts[1]})
+ }
+ return token
+ }
+
+ if strings.HasPrefix(arg, "-") {
+ if len(arg) == 1 {
+ return &Token{Index: p.argi, Type: TokenShort}
+ }
+ shortRune, size := utf8.DecodeRuneInString(arg[1:])
+ short := string(shortRune)
+ flag, ok := p.flags.short[short]
+ // Not a known short flag, we'll just return it anyway.
+ if !ok {
+ } else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() {
+ // Bool short flag.
+ } else {
+ // Short flag with combined argument: -fARG
+ token := &Token{p.argi, TokenShort, short}
+ if len(arg) > size+1 {
+ p.Push(&Token{p.argi, TokenArg, arg[size+1:]})
+ }
+ return token
+ }
+
+ if len(arg) > size+1 {
+ p.args = append([]string{"-" + arg[size+1:]}, p.args...)
+ }
+ return &Token{p.argi, TokenShort, short}
+ } else if strings.HasPrefix(arg, "@") {
+ expanded, err := ExpandArgsFromFile(arg[1:])
+ if err != nil {
+ return &Token{p.argi, TokenError, err.Error()}
+ }
+ if len(p.args) == 0 {
+ p.args = append(p.args, expanded...)
+ } else if p.argi >= len(p.args) {
+ p.args = append(p.args[:p.argi-1], expanded...)
+ } else {
+ p.args = append(p.args[:p.argi-1], append(expanded, p.args[p.argi+1:]...)...)
+ }
+ return p.Next()
+ }
+
+ return &Token{p.argi, TokenArg, arg}
+}
+
+func (p *ParseContext) Peek() *Token {
+ if len(p.peek) == 0 {
+ return p.Push(p.Next())
+ }
+ return p.peek[len(p.peek)-1]
+}
+
+func (p *ParseContext) Push(token *Token) *Token {
+ p.peek = append(p.peek, token)
+ return token
+}
+
+func (p *ParseContext) pop() *Token {
+ end := len(p.peek) - 1
+ token := p.peek[end]
+ p.peek = p.peek[0:end]
+ return token
+}
+
+func (p *ParseContext) String() string {
+ return p.SelectedCommand.FullCommand()
+}
+
+func (p *ParseContext) matchedFlag(flag *FlagClause, value string) {
+ p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value})
+}
+
+func (p *ParseContext) matchedArg(arg *ArgClause, value string) {
+ p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value})
+}
+
+func (p *ParseContext) matchedCmd(cmd *CmdClause) {
+ p.Elements = append(p.Elements, &ParseElement{Clause: cmd})
+ p.mergeFlags(cmd.flagGroup)
+ p.mergeArgs(cmd.argGroup)
+ p.SelectedCommand = cmd
+}
+
+// Expand arguments from a file. Lines starting with # will be treated as comments.
+func ExpandArgsFromFile(filename string) (out []string, err error) {
+ r, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "#") {
+ continue
+ }
+ out = append(out, line)
+ }
+ err = scanner.Err()
+ return
+}
+
+func parse(context *ParseContext, app *Application) (err error) {
+ context.mergeFlags(app.flagGroup)
+ context.mergeArgs(app.argGroup)
+
+ cmds := app.cmdGroup
+ ignoreDefault := context.ignoreDefault
+
+loop:
+ for !context.EOL() {
+ token := context.Peek()
+
+ switch token.Type {
+ case TokenLong, TokenShort:
+ if flag, err := context.flags.parse(context); err != nil {
+ if !ignoreDefault {
+ if cmd := cmds.defaultSubcommand(); cmd != nil {
+ cmd.completionAlts = cmds.cmdNames()
+ context.matchedCmd(cmd)
+ cmds = cmd.cmdGroup
+ break
+ }
+ }
+ return err
+ } else if flag == HelpFlag {
+ ignoreDefault = true
+ }
+
+ case TokenArg:
+ if cmds.have() {
+ selectedDefault := false
+ cmd, ok := cmds.commands[token.String()]
+ if !ok {
+ if !ignoreDefault {
+ if cmd = cmds.defaultSubcommand(); cmd != nil {
+ cmd.completionAlts = cmds.cmdNames()
+ selectedDefault = true
+ }
+ }
+ if cmd == nil {
+ return fmt.Errorf("expected command but got %q", token)
+ }
+ }
+ if cmd == HelpCommand {
+ ignoreDefault = true
+ }
+ cmd.completionAlts = nil
+ context.matchedCmd(cmd)
+ cmds = cmd.cmdGroup
+ if !selectedDefault {
+ context.Next()
+ }
+ } else if context.arguments.have() {
+ if app.noInterspersed {
+ // no more flags
+ context.argsOnly = true
+ }
+ arg := context.nextArg()
+ if arg == nil {
+ break loop
+ }
+ context.matchedArg(arg, token.String())
+ context.Next()
+ } else {
+ break loop
+ }
+
+ case TokenEOL:
+ break loop
+ }
+ }
+
+ // Move to innermost default command.
+ for !ignoreDefault {
+ if cmd := cmds.defaultSubcommand(); cmd != nil {
+ cmd.completionAlts = cmds.cmdNames()
+ context.matchedCmd(cmd)
+ cmds = cmd.cmdGroup
+ } else {
+ break
+ }
+ }
+
+ if !context.EOL() {
+ return fmt.Errorf("unexpected %s", context.Peek())
+ }
+
+ // Set defaults for all remaining args.
+ for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() {
+ for _, defaultValue := range arg.defaultValues {
+ if err := arg.value.Set(defaultValue); err != nil {
+ return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name)
+ }
+ }
+ }
+
+ return
+}
--- /dev/null
+package kingpin
+
+import (
+ "net"
+ "net/url"
+ "os"
+ "time"
+
+ "github.com/alecthomas/units"
+)
+
+type Settings interface {
+ SetValue(value Value)
+}
+
+type parserMixin struct {
+ value Value
+ required bool
+}
+
+func (p *parserMixin) SetValue(value Value) {
+ p.value = value
+}
+
+// StringMap provides key=value parsing into a map.
+func (p *parserMixin) StringMap() (target *map[string]string) {
+ target = &(map[string]string{})
+ p.StringMapVar(target)
+ return
+}
+
+// Duration sets the parser to a time.Duration parser.
+func (p *parserMixin) Duration() (target *time.Duration) {
+ target = new(time.Duration)
+ p.DurationVar(target)
+ return
+}
+
+// Bytes parses numeric byte units. eg. 1.5KB
+func (p *parserMixin) Bytes() (target *units.Base2Bytes) {
+ target = new(units.Base2Bytes)
+ p.BytesVar(target)
+ return
+}
+
+// IP sets the parser to a net.IP parser.
+func (p *parserMixin) IP() (target *net.IP) {
+ target = new(net.IP)
+ p.IPVar(target)
+ return
+}
+
+// TCP (host:port) address.
+func (p *parserMixin) TCP() (target **net.TCPAddr) {
+ target = new(*net.TCPAddr)
+ p.TCPVar(target)
+ return
+}
+
+// TCPVar (host:port) address.
+func (p *parserMixin) TCPVar(target **net.TCPAddr) {
+ p.SetValue(newTCPAddrValue(target))
+}
+
+// ExistingFile sets the parser to one that requires and returns an existing file.
+func (p *parserMixin) ExistingFile() (target *string) {
+ target = new(string)
+ p.ExistingFileVar(target)
+ return
+}
+
+// ExistingDir sets the parser to one that requires and returns an existing directory.
+func (p *parserMixin) ExistingDir() (target *string) {
+ target = new(string)
+ p.ExistingDirVar(target)
+ return
+}
+
+// ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory.
+func (p *parserMixin) ExistingFileOrDir() (target *string) {
+ target = new(string)
+ p.ExistingFileOrDirVar(target)
+ return
+}
+
+// File returns an os.File against an existing file.
+func (p *parserMixin) File() (target **os.File) {
+ target = new(*os.File)
+ p.FileVar(target)
+ return
+}
+
+// File attempts to open a File with os.OpenFile(flag, perm).
+func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) {
+ target = new(*os.File)
+ p.OpenFileVar(target, flag, perm)
+ return
+}
+
+// URL provides a valid, parsed url.URL.
+func (p *parserMixin) URL() (target **url.URL) {
+ target = new(*url.URL)
+ p.URLVar(target)
+ return
+}
+
+// StringMap provides key=value parsing into a map.
+func (p *parserMixin) StringMapVar(target *map[string]string) {
+ p.SetValue(newStringMapValue(target))
+}
+
+// Float sets the parser to a float64 parser.
+func (p *parserMixin) Float() (target *float64) {
+ return p.Float64()
+}
+
+// Float sets the parser to a float64 parser.
+func (p *parserMixin) FloatVar(target *float64) {
+ p.Float64Var(target)
+}
+
+// Duration sets the parser to a time.Duration parser.
+func (p *parserMixin) DurationVar(target *time.Duration) {
+ p.SetValue(newDurationValue(target))
+}
+
+// BytesVar parses numeric byte units. eg. 1.5KB
+func (p *parserMixin) BytesVar(target *units.Base2Bytes) {
+ p.SetValue(newBytesValue(target))
+}
+
+// IP sets the parser to a net.IP parser.
+func (p *parserMixin) IPVar(target *net.IP) {
+ p.SetValue(newIPValue(target))
+}
+
+// ExistingFile sets the parser to one that requires and returns an existing file.
+func (p *parserMixin) ExistingFileVar(target *string) {
+ p.SetValue(newExistingFileValue(target))
+}
+
+// ExistingDir sets the parser to one that requires and returns an existing directory.
+func (p *parserMixin) ExistingDirVar(target *string) {
+ p.SetValue(newExistingDirValue(target))
+}
+
+// ExistingDir sets the parser to one that requires and returns an existing directory.
+func (p *parserMixin) ExistingFileOrDirVar(target *string) {
+ p.SetValue(newExistingFileOrDirValue(target))
+}
+
+// FileVar opens an existing file.
+func (p *parserMixin) FileVar(target **os.File) {
+ p.SetValue(newFileValue(target, os.O_RDONLY, 0))
+}
+
+// OpenFileVar calls os.OpenFile(flag, perm)
+func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) {
+ p.SetValue(newFileValue(target, flag, perm))
+}
+
+// URL provides a valid, parsed url.URL.
+func (p *parserMixin) URLVar(target **url.URL) {
+ p.SetValue(newURLValue(target))
+}
+
+// URLList provides a parsed list of url.URL values.
+func (p *parserMixin) URLList() (target *[]*url.URL) {
+ target = new([]*url.URL)
+ p.URLListVar(target)
+ return
+}
+
+// URLListVar provides a parsed list of url.URL values.
+func (p *parserMixin) URLListVar(target *[]*url.URL) {
+ p.SetValue(newURLListValue(target))
+}
+
+// Enum allows a value from a set of options.
+func (p *parserMixin) Enum(options ...string) (target *string) {
+ target = new(string)
+ p.EnumVar(target, options...)
+ return
+}
+
+// EnumVar allows a value from a set of options.
+func (p *parserMixin) EnumVar(target *string, options ...string) {
+ p.SetValue(newEnumFlag(target, options...))
+}
+
+// Enums allows a set of values from a set of options.
+func (p *parserMixin) Enums(options ...string) (target *[]string) {
+ target = new([]string)
+ p.EnumsVar(target, options...)
+ return
+}
+
+// EnumVar allows a value from a set of options.
+func (p *parserMixin) EnumsVar(target *[]string, options ...string) {
+ p.SetValue(newEnumsFlag(target, options...))
+}
+
+// A Counter increments a number each time it is encountered.
+func (p *parserMixin) Counter() (target *int) {
+ target = new(int)
+ p.CounterVar(target)
+ return
+}
+
+func (p *parserMixin) CounterVar(target *int) {
+ p.SetValue(newCounterValue(target))
+}
--- /dev/null
+package kingpin
+
+// Default usage template.
+var DefaultUsageTemplate = `{{define "FormatCommand"}}\
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
+{{end}}\
+
+{{define "FormatCommands"}}\
+{{range .FlattenedCommands}}\
+{{if not .Hidden}}\
+ {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
+{{.Help|Wrap 4}}
+{{end}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatUsage"}}\
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
+{{if .Help}}
+{{.Help|Wrap 0}}\
+{{end}}\
+
+{{end}}\
+
+{{if .Context.SelectedCommand}}\
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
+{{else}}\
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
+{{end}}\
+{{if .Context.Flags}}\
+Flags:
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.Args}}\
+Args:
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.SelectedCommand}}\
+{{if len .Context.SelectedCommand.Commands}}\
+Subcommands:
+{{template "FormatCommands" .Context.SelectedCommand}}
+{{end}}\
+{{else if .App.Commands}}\
+Commands:
+{{template "FormatCommands" .App}}
+{{end}}\
+`
+
+// Usage template where command's optional flags are listed separately
+var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
+{{end}}\
+
+{{define "FormatCommands"}}\
+{{range .FlattenedCommands}}\
+{{if not .Hidden}}\
+ {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
+{{.Help|Wrap 4}}
+{{end}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatUsage"}}\
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
+{{if .Help}}
+{{.Help|Wrap 0}}\
+{{end}}\
+
+{{end}}\
+{{if .Context.SelectedCommand}}\
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
+{{else}}\
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
+{{end}}\
+
+{{if .Context.Flags|RequiredFlags}}\
+Required flags:
+{{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.Flags|OptionalFlags}}\
+Optional flags:
+{{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.Args}}\
+Args:
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.SelectedCommand}}\
+Subcommands:
+{{if .Context.SelectedCommand.Commands}}\
+{{template "FormatCommands" .Context.SelectedCommand}}
+{{end}}\
+{{else if .App.Commands}}\
+Commands:
+{{template "FormatCommands" .App}}
+{{end}}\
+`
+
+// Usage template with compactly formatted commands.
+var CompactUsageTemplate = `{{define "FormatCommand"}}\
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
+{{end}}\
+
+{{define "FormatCommandList"}}\
+{{range .}}\
+{{if not .Hidden}}\
+{{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}}
+{{end}}\
+{{template "FormatCommandList" .Commands}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatUsage"}}\
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
+{{if .Help}}
+{{.Help|Wrap 0}}\
+{{end}}\
+
+{{end}}\
+
+{{if .Context.SelectedCommand}}\
+usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}}
+{{else}}\
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
+{{end}}\
+{{if .Context.Flags}}\
+Flags:
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.Args}}\
+Args:
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.SelectedCommand}}\
+{{if .Context.SelectedCommand.Commands}}\
+Commands:
+ {{.Context.SelectedCommand}}
+{{template "FormatCommandList" .Context.SelectedCommand.Commands}}
+{{end}}\
+{{else if .App.Commands}}\
+Commands:
+{{template "FormatCommandList" .App.Commands}}
+{{end}}\
+`
+
+var ManPageTemplate = `{{define "FormatFlags"}}\
+{{range .Flags}}\
+{{if not .Hidden}}\
+.TP
+\fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR
+{{.Help}}
+{{end}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatCommand"}}\
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
+{{end}}\
+
+{{define "FormatCommands"}}\
+{{range .FlattenedCommands}}\
+{{if not .Hidden}}\
+.SS
+\fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR
+.PP
+{{.Help}}
+{{template "FormatFlags" .}}\
+{{end}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatUsage"}}\
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}\\fR
+{{end}}\
+
+.TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}"
+.SH "NAME"
+{{.App.Name}}
+.SH "SYNOPSIS"
+.TP
+\fB{{.App.Name}}{{template "FormatUsage" .App}}
+.SH "DESCRIPTION"
+{{.App.Help}}
+.SH "OPTIONS"
+{{template "FormatFlags" .App}}\
+{{if .App.Commands}}\
+.SH "COMMANDS"
+{{template "FormatCommands" .App}}\
+{{end}}\
+`
+
+// Default usage template.
+var LongHelpTemplate = `{{define "FormatCommand"}}\
+{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
+{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
+{{end}}\
+
+{{define "FormatCommands"}}\
+{{range .FlattenedCommands}}\
+{{if not .Hidden}}\
+ {{.FullCommand}}{{template "FormatCommand" .}}
+{{.Help|Wrap 4}}
+{{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}}
+{{end}}\
+{{end}}\
+{{end}}\
+
+{{define "FormatUsage"}}\
+{{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}}
+{{if .Help}}
+{{.Help|Wrap 0}}\
+{{end}}\
+
+{{end}}\
+
+usage: {{.App.Name}}{{template "FormatUsage" .App}}
+{{if .Context.Flags}}\
+Flags:
+{{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .Context.Args}}\
+Args:
+{{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}}
+{{end}}\
+{{if .App.Commands}}\
+Commands:
+{{template "FormatCommands" .App}}
+{{end}}\
+`
+
+var BashCompletionTemplate = `
+_{{.App.Name}}_bash_autocomplete() {
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+ return 0
+}
+complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
+
+`
+
+var ZshCompletionTemplate = `
+#compdef {{.App.Name}}
+autoload -U compinit && compinit
+autoload -U bashcompinit && bashcompinit
+
+_{{.App.Name}}_bash_autocomplete() {
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} )
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+ return 0
+}
+complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}}
+`
--- /dev/null
+package kingpin
+
+import (
+ "bytes"
+ "fmt"
+ "go/doc"
+ "io"
+ "strings"
+
+ "github.com/alecthomas/template"
+)
+
+var (
+ preIndent = " "
+)
+
+func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) {
+ // Find size of first column.
+ s := 0
+ for _, row := range rows {
+ if c := len(row[0]); c > s && c < 30 {
+ s = c
+ }
+ }
+
+ indentStr := strings.Repeat(" ", indent)
+ offsetStr := strings.Repeat(" ", s+padding)
+
+ for _, row := range rows {
+ buf := bytes.NewBuffer(nil)
+ doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent)
+ lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
+ fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "")
+ if len(row[0]) >= 30 {
+ fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr)
+ }
+ fmt.Fprintf(w, "%s\n", lines[0])
+ for _, line := range lines[1:] {
+ fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line)
+ }
+ }
+}
+
+// Usage writes application usage to w. It parses args to determine
+// appropriate help context, such as which command to show help for.
+func (a *Application) Usage(args []string) {
+ context, err := a.parseContext(true, args)
+ a.FatalIfError(err, "")
+ if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil {
+ panic(err)
+ }
+}
+
+func formatAppUsage(app *ApplicationModel) string {
+ s := []string{app.Name}
+ if len(app.Flags) > 0 {
+ s = append(s, app.FlagSummary())
+ }
+ if len(app.Args) > 0 {
+ s = append(s, app.ArgSummary())
+ }
+ return strings.Join(s, " ")
+}
+
+func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string {
+ s := []string{app.Name, cmd.String()}
+ if len(app.Flags) > 0 {
+ s = append(s, app.FlagSummary())
+ }
+ if len(app.Args) > 0 {
+ s = append(s, app.ArgSummary())
+ }
+ return strings.Join(s, " ")
+}
+
+func formatFlag(haveShort bool, flag *FlagModel) string {
+ flagString := ""
+ if flag.Short != 0 {
+ flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name)
+ } else {
+ if haveShort {
+ flagString += fmt.Sprintf(" --%s", flag.Name)
+ } else {
+ flagString += fmt.Sprintf("--%s", flag.Name)
+ }
+ }
+ if !flag.IsBoolFlag() {
+ flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder())
+ }
+ if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() {
+ flagString += " ..."
+ }
+ return flagString
+}
+
+type templateParseContext struct {
+ SelectedCommand *CmdModel
+ *FlagGroupModel
+ *ArgGroupModel
+}
+
+type templateContext struct {
+ App *ApplicationModel
+ Width int
+ Context *templateParseContext
+}
+
+// UsageForContext displays usage information from a ParseContext (obtained from
+// Application.ParseContext() or Action(f) callbacks).
+func (a *Application) UsageForContext(context *ParseContext) error {
+ return a.UsageForContextWithTemplate(context, 2, a.usageTemplate)
+}
+
+// UsageForContextWithTemplate is the base usage function. You generally don't need to use this.
+func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error {
+ width := guessWidth(a.usageWriter)
+ funcs := template.FuncMap{
+ "Indent": func(level int) string {
+ return strings.Repeat(" ", level*indent)
+ },
+ "Wrap": func(indent int, s string) string {
+ buf := bytes.NewBuffer(nil)
+ indentText := strings.Repeat(" ", indent)
+ doc.ToText(buf, s, indentText, " "+indentText, width-indent)
+ return buf.String()
+ },
+ "FormatFlag": formatFlag,
+ "FlagsToTwoColumns": func(f []*FlagModel) [][2]string {
+ rows := [][2]string{}
+ haveShort := false
+ for _, flag := range f {
+ if flag.Short != 0 {
+ haveShort = true
+ break
+ }
+ }
+ for _, flag := range f {
+ if !flag.Hidden {
+ rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help})
+ }
+ }
+ return rows
+ },
+ "RequiredFlags": func(f []*FlagModel) []*FlagModel {
+ requiredFlags := []*FlagModel{}
+ for _, flag := range f {
+ if flag.Required {
+ requiredFlags = append(requiredFlags, flag)
+ }
+ }
+ return requiredFlags
+ },
+ "OptionalFlags": func(f []*FlagModel) []*FlagModel {
+ optionalFlags := []*FlagModel{}
+ for _, flag := range f {
+ if !flag.Required {
+ optionalFlags = append(optionalFlags, flag)
+ }
+ }
+ return optionalFlags
+ },
+ "ArgsToTwoColumns": func(a []*ArgModel) [][2]string {
+ rows := [][2]string{}
+ for _, arg := range a {
+ s := "<" + arg.Name + ">"
+ if !arg.Required {
+ s = "[" + s + "]"
+ }
+ rows = append(rows, [2]string{s, arg.Help})
+ }
+ return rows
+ },
+ "FormatTwoColumns": func(rows [][2]string) string {
+ buf := bytes.NewBuffer(nil)
+ formatTwoColumns(buf, indent, indent, width, rows)
+ return buf.String()
+ },
+ "FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string {
+ buf := bytes.NewBuffer(nil)
+ formatTwoColumns(buf, indent, padding, width, rows)
+ return buf.String()
+ },
+ "FormatAppUsage": formatAppUsage,
+ "FormatCommandUsage": formatCmdUsage,
+ "IsCumulative": func(value Value) bool {
+ r, ok := value.(remainderArg)
+ return ok && r.IsCumulative()
+ },
+ "Char": func(c rune) string {
+ return string(c)
+ },
+ }
+ t, err := template.New("usage").Funcs(funcs).Parse(tmpl)
+ if err != nil {
+ return err
+ }
+ var selectedCommand *CmdModel
+ if context.SelectedCommand != nil {
+ selectedCommand = context.SelectedCommand.Model()
+ }
+ ctx := templateContext{
+ App: a.Model(),
+ Width: width,
+ Context: &templateParseContext{
+ SelectedCommand: selectedCommand,
+ FlagGroupModel: context.flags.Model(),
+ ArgGroupModel: context.arguments.Model(),
+ },
+ }
+ return t.Execute(a.usageWriter, ctx)
+}
--- /dev/null
+package kingpin
+
+//go:generate go run ./cmd/genvalues/main.go
+
+import (
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "reflect"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/alecthomas/units"
+)
+
+// NOTE: Most of the base type values were lifted from:
+// http://golang.org/src/pkg/flag/flag.go?s=20146:20222
+
+// Value is the interface to the dynamic value stored in a flag.
+// (The default value is represented as a string.)
+//
+// If a Value has an IsBoolFlag() bool method returning true, the command-line
+// parser makes --name equivalent to -name=true rather than using the next
+// command-line argument, and adds a --no-name counterpart for negating the
+// flag.
+type Value interface {
+ String() string
+ Set(string) error
+}
+
+// Getter is an interface that allows the contents of a Value to be retrieved.
+// It wraps the Value interface, rather than being part of it, because it
+// appeared after Go 1 and its compatibility rules. All Value types provided
+// by this package satisfy the Getter interface.
+type Getter interface {
+ Value
+ Get() interface{}
+}
+
+// Optional interface to indicate boolean flags that don't accept a value, and
+// implicitly have a --no-<x> negation counterpart.
+type boolFlag interface {
+ Value
+ IsBoolFlag() bool
+}
+
+// Optional interface for arguments that cumulatively consume all remaining
+// input.
+type remainderArg interface {
+ Value
+ IsCumulative() bool
+}
+
+// Optional interface for flags that can be repeated.
+type repeatableFlag interface {
+ Value
+ IsCumulative() bool
+}
+
+type accumulator struct {
+ element func(value interface{}) Value
+ typ reflect.Type
+ slice reflect.Value
+}
+
+// Use reflection to accumulate values into a slice.
+//
+// target := []string{}
+// newAccumulator(&target, func (value interface{}) Value {
+// return newStringValue(value.(*string))
+// })
+func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator {
+ typ := reflect.TypeOf(slice)
+ if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice {
+ panic("expected a pointer to a slice")
+ }
+ return &accumulator{
+ element: element,
+ typ: typ.Elem().Elem(),
+ slice: reflect.ValueOf(slice),
+ }
+}
+
+func (a *accumulator) String() string {
+ out := []string{}
+ s := a.slice.Elem()
+ for i := 0; i < s.Len(); i++ {
+ out = append(out, a.element(s.Index(i).Addr().Interface()).String())
+ }
+ return strings.Join(out, ",")
+}
+
+func (a *accumulator) Set(value string) error {
+ e := reflect.New(a.typ)
+ if err := a.element(e.Interface()).Set(value); err != nil {
+ return err
+ }
+ slice := reflect.Append(a.slice.Elem(), e.Elem())
+ a.slice.Elem().Set(slice)
+ return nil
+}
+
+func (a *accumulator) Get() interface{} {
+ return a.slice.Interface()
+}
+
+func (a *accumulator) IsCumulative() bool {
+ return true
+}
+
+func (b *boolValue) IsBoolFlag() bool { return true }
+
+// -- time.Duration Value
+type durationValue time.Duration
+
+func newDurationValue(p *time.Duration) *durationValue {
+ return (*durationValue)(p)
+}
+
+func (d *durationValue) Set(s string) error {
+ v, err := time.ParseDuration(s)
+ *d = durationValue(v)
+ return err
+}
+
+func (d *durationValue) Get() interface{} { return time.Duration(*d) }
+
+func (d *durationValue) String() string { return (*time.Duration)(d).String() }
+
+// -- map[string]string Value
+type stringMapValue map[string]string
+
+func newStringMapValue(p *map[string]string) *stringMapValue {
+ return (*stringMapValue)(p)
+}
+
+var stringMapRegex = regexp.MustCompile("[:=]")
+
+func (s *stringMapValue) Set(value string) error {
+ parts := stringMapRegex.Split(value, 2)
+ if len(parts) != 2 {
+ return fmt.Errorf("expected KEY=VALUE got '%s'", value)
+ }
+ (*s)[parts[0]] = parts[1]
+ return nil
+}
+
+func (s *stringMapValue) Get() interface{} {
+ return (map[string]string)(*s)
+}
+
+func (s *stringMapValue) String() string {
+ return fmt.Sprintf("%s", map[string]string(*s))
+}
+
+func (s *stringMapValue) IsCumulative() bool {
+ return true
+}
+
+// -- net.IP Value
+type ipValue net.IP
+
+func newIPValue(p *net.IP) *ipValue {
+ return (*ipValue)(p)
+}
+
+func (i *ipValue) Set(value string) error {
+ if ip := net.ParseIP(value); ip == nil {
+ return fmt.Errorf("'%s' is not an IP address", value)
+ } else {
+ *i = *(*ipValue)(&ip)
+ return nil
+ }
+}
+
+func (i *ipValue) Get() interface{} {
+ return (net.IP)(*i)
+}
+
+func (i *ipValue) String() string {
+ return (*net.IP)(i).String()
+}
+
+// -- *net.TCPAddr Value
+type tcpAddrValue struct {
+ addr **net.TCPAddr
+}
+
+func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue {
+ return &tcpAddrValue{p}
+}
+
+func (i *tcpAddrValue) Set(value string) error {
+ if addr, err := net.ResolveTCPAddr("tcp", value); err != nil {
+ return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err)
+ } else {
+ *i.addr = addr
+ return nil
+ }
+}
+
+func (t *tcpAddrValue) Get() interface{} {
+ return (*net.TCPAddr)(*t.addr)
+}
+
+func (i *tcpAddrValue) String() string {
+ return (*i.addr).String()
+}
+
+// -- existingFile Value
+
+type fileStatValue struct {
+ path *string
+ predicate func(os.FileInfo) error
+}
+
+func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue {
+ return &fileStatValue{
+ path: p,
+ predicate: predicate,
+ }
+}
+
+func (e *fileStatValue) Set(value string) error {
+ if s, err := os.Stat(value); os.IsNotExist(err) {
+ return fmt.Errorf("path '%s' does not exist", value)
+ } else if err != nil {
+ return err
+ } else if err := e.predicate(s); err != nil {
+ return err
+ }
+ *e.path = value
+ return nil
+}
+
+func (f *fileStatValue) Get() interface{} {
+ return (string)(*f.path)
+}
+
+func (e *fileStatValue) String() string {
+ return *e.path
+}
+
+// -- os.File value
+
+type fileValue struct {
+ f **os.File
+ flag int
+ perm os.FileMode
+}
+
+func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue {
+ return &fileValue{p, flag, perm}
+}
+
+func (f *fileValue) Set(value string) error {
+ if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil {
+ return err
+ } else {
+ *f.f = fd
+ return nil
+ }
+}
+
+func (f *fileValue) Get() interface{} {
+ return (*os.File)(*f.f)
+}
+
+func (f *fileValue) String() string {
+ if *f.f == nil {
+ return "<nil>"
+ }
+ return (*f.f).Name()
+}
+
+// -- url.URL Value
+type urlValue struct {
+ u **url.URL
+}
+
+func newURLValue(p **url.URL) *urlValue {
+ return &urlValue{p}
+}
+
+func (u *urlValue) Set(value string) error {
+ if url, err := url.Parse(value); err != nil {
+ return fmt.Errorf("invalid URL: %s", err)
+ } else {
+ *u.u = url
+ return nil
+ }
+}
+
+func (u *urlValue) Get() interface{} {
+ return (*url.URL)(*u.u)
+}
+
+func (u *urlValue) String() string {
+ if *u.u == nil {
+ return "<nil>"
+ }
+ return (*u.u).String()
+}
+
+// -- []*url.URL Value
+type urlListValue []*url.URL
+
+func newURLListValue(p *[]*url.URL) *urlListValue {
+ return (*urlListValue)(p)
+}
+
+func (u *urlListValue) Set(value string) error {
+ if url, err := url.Parse(value); err != nil {
+ return fmt.Errorf("invalid URL: %s", err)
+ } else {
+ *u = append(*u, url)
+ return nil
+ }
+}
+
+func (u *urlListValue) Get() interface{} {
+ return ([]*url.URL)(*u)
+}
+
+func (u *urlListValue) String() string {
+ out := []string{}
+ for _, url := range *u {
+ out = append(out, url.String())
+ }
+ return strings.Join(out, ",")
+}
+
+func (u *urlListValue) IsCumulative() bool {
+ return true
+}
+
+// A flag whose value must be in a set of options.
+type enumValue struct {
+ value *string
+ options []string
+}
+
+func newEnumFlag(target *string, options ...string) *enumValue {
+ return &enumValue{
+ value: target,
+ options: options,
+ }
+}
+
+func (a *enumValue) String() string {
+ return *a.value
+}
+
+func (a *enumValue) Set(value string) error {
+ for _, v := range a.options {
+ if v == value {
+ *a.value = value
+ return nil
+ }
+ }
+ return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value)
+}
+
+func (e *enumValue) Get() interface{} {
+ return (string)(*e.value)
+}
+
+// -- []string Enum Value
+type enumsValue struct {
+ value *[]string
+ options []string
+}
+
+func newEnumsFlag(target *[]string, options ...string) *enumsValue {
+ return &enumsValue{
+ value: target,
+ options: options,
+ }
+}
+
+func (s *enumsValue) Set(value string) error {
+ for _, v := range s.options {
+ if v == value {
+ *s.value = append(*s.value, value)
+ return nil
+ }
+ }
+ return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value)
+}
+
+func (e *enumsValue) Get() interface{} {
+ return ([]string)(*e.value)
+}
+
+func (s *enumsValue) String() string {
+ return strings.Join(*s.value, ",")
+}
+
+func (s *enumsValue) IsCumulative() bool {
+ return true
+}
+
+// -- units.Base2Bytes Value
+type bytesValue units.Base2Bytes
+
+func newBytesValue(p *units.Base2Bytes) *bytesValue {
+ return (*bytesValue)(p)
+}
+
+func (d *bytesValue) Set(s string) error {
+ v, err := units.ParseBase2Bytes(s)
+ *d = bytesValue(v)
+ return err
+}
+
+func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) }
+
+func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() }
+
+func newExistingFileValue(target *string) *fileStatValue {
+ return newFileStatValue(target, func(s os.FileInfo) error {
+ if s.IsDir() {
+ return fmt.Errorf("'%s' is a directory", s.Name())
+ }
+ return nil
+ })
+}
+
+func newExistingDirValue(target *string) *fileStatValue {
+ return newFileStatValue(target, func(s os.FileInfo) error {
+ if !s.IsDir() {
+ return fmt.Errorf("'%s' is a file", s.Name())
+ }
+ return nil
+ })
+}
+
+func newExistingFileOrDirValue(target *string) *fileStatValue {
+ return newFileStatValue(target, func(s os.FileInfo) error { return nil })
+}
+
+type counterValue int
+
+func newCounterValue(n *int) *counterValue {
+ return (*counterValue)(n)
+}
+
+func (c *counterValue) Set(s string) error {
+ *c++
+ return nil
+}
+
+func (c *counterValue) Get() interface{} { return (int)(*c) }
+func (c *counterValue) IsBoolFlag() bool { return true }
+func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) }
+func (c *counterValue) IsCumulative() bool { return true }
+
+func resolveHost(value string) (net.IP, error) {
+ if ip := net.ParseIP(value); ip != nil {
+ return ip, nil
+ } else {
+ if addr, err := net.ResolveIPAddr("ip", value); err != nil {
+ return nil, err
+ } else {
+ return addr.IP, nil
+ }
+ }
+}
--- /dev/null
+[
+ {"type": "bool", "parser": "strconv.ParseBool(s)"},
+ {"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"},
+ {"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"},
+ {"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"},
+ {"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"},
+ {"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"},
+ {"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"},
+ {"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"},
+ {"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"},
+ {"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"},
+ {"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"},
+ {"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"},
+ {"type": "float64", "parser": "strconv.ParseFloat(s, 64)"},
+ {"type": "float32", "parser": "strconv.ParseFloat(s, 32)"},
+ {"name": "Duration", "type": "time.Duration", "no_value_parser": true},
+ {"name": "IP", "type": "net.IP", "no_value_parser": true},
+ {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true},
+ {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true},
+ {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true},
+ {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true},
+ {"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"},
+ {"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."},
+ {"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."}
+]
--- /dev/null
+package kingpin
+
+import (
+ "encoding/hex"
+ "fmt"
+ "net"
+ "regexp"
+ "strconv"
+ "time"
+)
+
+// This file is autogenerated by "go generate .". Do not modify.
+
+// -- bool Value
+type boolValue struct{ v *bool }
+
+func newBoolValue(p *bool) *boolValue {
+ return &boolValue{p}
+}
+
+func (f *boolValue) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ if err == nil {
+ *f.v = (bool)(v)
+ }
+ return err
+}
+
+func (f *boolValue) Get() interface{} { return (bool)(*f.v) }
+
+func (f *boolValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Bool parses the next command-line value as bool.
+func (p *parserMixin) Bool() (target *bool) {
+ target = new(bool)
+ p.BoolVar(target)
+ return
+}
+
+func (p *parserMixin) BoolVar(target *bool) {
+ p.SetValue(newBoolValue(target))
+}
+
+// BoolList accumulates bool values into a slice.
+func (p *parserMixin) BoolList() (target *[]bool) {
+ target = new([]bool)
+ p.BoolListVar(target)
+ return
+}
+
+func (p *parserMixin) BoolListVar(target *[]bool) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newBoolValue(v.(*bool))
+ }))
+}
+
+// -- string Value
+type stringValue struct{ v *string }
+
+func newStringValue(p *string) *stringValue {
+ return &stringValue{p}
+}
+
+func (f *stringValue) Set(s string) error {
+ v, err := s, error(nil)
+ if err == nil {
+ *f.v = (string)(v)
+ }
+ return err
+}
+
+func (f *stringValue) Get() interface{} { return (string)(*f.v) }
+
+func (f *stringValue) String() string { return string(*f.v) }
+
+// String parses the next command-line value as string.
+func (p *parserMixin) String() (target *string) {
+ target = new(string)
+ p.StringVar(target)
+ return
+}
+
+func (p *parserMixin) StringVar(target *string) {
+ p.SetValue(newStringValue(target))
+}
+
+// Strings accumulates string values into a slice.
+func (p *parserMixin) Strings() (target *[]string) {
+ target = new([]string)
+ p.StringsVar(target)
+ return
+}
+
+func (p *parserMixin) StringsVar(target *[]string) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newStringValue(v.(*string))
+ }))
+}
+
+// -- uint Value
+type uintValue struct{ v *uint }
+
+func newUintValue(p *uint) *uintValue {
+ return &uintValue{p}
+}
+
+func (f *uintValue) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
+ if err == nil {
+ *f.v = (uint)(v)
+ }
+ return err
+}
+
+func (f *uintValue) Get() interface{} { return (uint)(*f.v) }
+
+func (f *uintValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Uint parses the next command-line value as uint.
+func (p *parserMixin) Uint() (target *uint) {
+ target = new(uint)
+ p.UintVar(target)
+ return
+}
+
+func (p *parserMixin) UintVar(target *uint) {
+ p.SetValue(newUintValue(target))
+}
+
+// Uints accumulates uint values into a slice.
+func (p *parserMixin) Uints() (target *[]uint) {
+ target = new([]uint)
+ p.UintsVar(target)
+ return
+}
+
+func (p *parserMixin) UintsVar(target *[]uint) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newUintValue(v.(*uint))
+ }))
+}
+
+// -- uint8 Value
+type uint8Value struct{ v *uint8 }
+
+func newUint8Value(p *uint8) *uint8Value {
+ return &uint8Value{p}
+}
+
+func (f *uint8Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 8)
+ if err == nil {
+ *f.v = (uint8)(v)
+ }
+ return err
+}
+
+func (f *uint8Value) Get() interface{} { return (uint8)(*f.v) }
+
+func (f *uint8Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Uint8 parses the next command-line value as uint8.
+func (p *parserMixin) Uint8() (target *uint8) {
+ target = new(uint8)
+ p.Uint8Var(target)
+ return
+}
+
+func (p *parserMixin) Uint8Var(target *uint8) {
+ p.SetValue(newUint8Value(target))
+}
+
+// Uint8List accumulates uint8 values into a slice.
+func (p *parserMixin) Uint8List() (target *[]uint8) {
+ target = new([]uint8)
+ p.Uint8ListVar(target)
+ return
+}
+
+func (p *parserMixin) Uint8ListVar(target *[]uint8) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newUint8Value(v.(*uint8))
+ }))
+}
+
+// -- uint16 Value
+type uint16Value struct{ v *uint16 }
+
+func newUint16Value(p *uint16) *uint16Value {
+ return &uint16Value{p}
+}
+
+func (f *uint16Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 16)
+ if err == nil {
+ *f.v = (uint16)(v)
+ }
+ return err
+}
+
+func (f *uint16Value) Get() interface{} { return (uint16)(*f.v) }
+
+func (f *uint16Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Uint16 parses the next command-line value as uint16.
+func (p *parserMixin) Uint16() (target *uint16) {
+ target = new(uint16)
+ p.Uint16Var(target)
+ return
+}
+
+func (p *parserMixin) Uint16Var(target *uint16) {
+ p.SetValue(newUint16Value(target))
+}
+
+// Uint16List accumulates uint16 values into a slice.
+func (p *parserMixin) Uint16List() (target *[]uint16) {
+ target = new([]uint16)
+ p.Uint16ListVar(target)
+ return
+}
+
+func (p *parserMixin) Uint16ListVar(target *[]uint16) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newUint16Value(v.(*uint16))
+ }))
+}
+
+// -- uint32 Value
+type uint32Value struct{ v *uint32 }
+
+func newUint32Value(p *uint32) *uint32Value {
+ return &uint32Value{p}
+}
+
+func (f *uint32Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 32)
+ if err == nil {
+ *f.v = (uint32)(v)
+ }
+ return err
+}
+
+func (f *uint32Value) Get() interface{} { return (uint32)(*f.v) }
+
+func (f *uint32Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Uint32 parses the next command-line value as uint32.
+func (p *parserMixin) Uint32() (target *uint32) {
+ target = new(uint32)
+ p.Uint32Var(target)
+ return
+}
+
+func (p *parserMixin) Uint32Var(target *uint32) {
+ p.SetValue(newUint32Value(target))
+}
+
+// Uint32List accumulates uint32 values into a slice.
+func (p *parserMixin) Uint32List() (target *[]uint32) {
+ target = new([]uint32)
+ p.Uint32ListVar(target)
+ return
+}
+
+func (p *parserMixin) Uint32ListVar(target *[]uint32) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newUint32Value(v.(*uint32))
+ }))
+}
+
+// -- uint64 Value
+type uint64Value struct{ v *uint64 }
+
+func newUint64Value(p *uint64) *uint64Value {
+ return &uint64Value{p}
+}
+
+func (f *uint64Value) Set(s string) error {
+ v, err := strconv.ParseUint(s, 0, 64)
+ if err == nil {
+ *f.v = (uint64)(v)
+ }
+ return err
+}
+
+func (f *uint64Value) Get() interface{} { return (uint64)(*f.v) }
+
+func (f *uint64Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Uint64 parses the next command-line value as uint64.
+func (p *parserMixin) Uint64() (target *uint64) {
+ target = new(uint64)
+ p.Uint64Var(target)
+ return
+}
+
+func (p *parserMixin) Uint64Var(target *uint64) {
+ p.SetValue(newUint64Value(target))
+}
+
+// Uint64List accumulates uint64 values into a slice.
+func (p *parserMixin) Uint64List() (target *[]uint64) {
+ target = new([]uint64)
+ p.Uint64ListVar(target)
+ return
+}
+
+func (p *parserMixin) Uint64ListVar(target *[]uint64) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newUint64Value(v.(*uint64))
+ }))
+}
+
+// -- int Value
+type intValue struct{ v *int }
+
+func newIntValue(p *int) *intValue {
+ return &intValue{p}
+}
+
+func (f *intValue) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 64)
+ if err == nil {
+ *f.v = (int)(v)
+ }
+ return err
+}
+
+func (f *intValue) Get() interface{} { return (int)(*f.v) }
+
+func (f *intValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Int parses the next command-line value as int.
+func (p *parserMixin) Int() (target *int) {
+ target = new(int)
+ p.IntVar(target)
+ return
+}
+
+func (p *parserMixin) IntVar(target *int) {
+ p.SetValue(newIntValue(target))
+}
+
+// Ints accumulates int values into a slice.
+func (p *parserMixin) Ints() (target *[]int) {
+ target = new([]int)
+ p.IntsVar(target)
+ return
+}
+
+func (p *parserMixin) IntsVar(target *[]int) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newIntValue(v.(*int))
+ }))
+}
+
+// -- int8 Value
+type int8Value struct{ v *int8 }
+
+func newInt8Value(p *int8) *int8Value {
+ return &int8Value{p}
+}
+
+func (f *int8Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 8)
+ if err == nil {
+ *f.v = (int8)(v)
+ }
+ return err
+}
+
+func (f *int8Value) Get() interface{} { return (int8)(*f.v) }
+
+func (f *int8Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Int8 parses the next command-line value as int8.
+func (p *parserMixin) Int8() (target *int8) {
+ target = new(int8)
+ p.Int8Var(target)
+ return
+}
+
+func (p *parserMixin) Int8Var(target *int8) {
+ p.SetValue(newInt8Value(target))
+}
+
+// Int8List accumulates int8 values into a slice.
+func (p *parserMixin) Int8List() (target *[]int8) {
+ target = new([]int8)
+ p.Int8ListVar(target)
+ return
+}
+
+func (p *parserMixin) Int8ListVar(target *[]int8) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newInt8Value(v.(*int8))
+ }))
+}
+
+// -- int16 Value
+type int16Value struct{ v *int16 }
+
+func newInt16Value(p *int16) *int16Value {
+ return &int16Value{p}
+}
+
+func (f *int16Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 16)
+ if err == nil {
+ *f.v = (int16)(v)
+ }
+ return err
+}
+
+func (f *int16Value) Get() interface{} { return (int16)(*f.v) }
+
+func (f *int16Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Int16 parses the next command-line value as int16.
+func (p *parserMixin) Int16() (target *int16) {
+ target = new(int16)
+ p.Int16Var(target)
+ return
+}
+
+func (p *parserMixin) Int16Var(target *int16) {
+ p.SetValue(newInt16Value(target))
+}
+
+// Int16List accumulates int16 values into a slice.
+func (p *parserMixin) Int16List() (target *[]int16) {
+ target = new([]int16)
+ p.Int16ListVar(target)
+ return
+}
+
+func (p *parserMixin) Int16ListVar(target *[]int16) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newInt16Value(v.(*int16))
+ }))
+}
+
+// -- int32 Value
+type int32Value struct{ v *int32 }
+
+func newInt32Value(p *int32) *int32Value {
+ return &int32Value{p}
+}
+
+func (f *int32Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 32)
+ if err == nil {
+ *f.v = (int32)(v)
+ }
+ return err
+}
+
+func (f *int32Value) Get() interface{} { return (int32)(*f.v) }
+
+func (f *int32Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Int32 parses the next command-line value as int32.
+func (p *parserMixin) Int32() (target *int32) {
+ target = new(int32)
+ p.Int32Var(target)
+ return
+}
+
+func (p *parserMixin) Int32Var(target *int32) {
+ p.SetValue(newInt32Value(target))
+}
+
+// Int32List accumulates int32 values into a slice.
+func (p *parserMixin) Int32List() (target *[]int32) {
+ target = new([]int32)
+ p.Int32ListVar(target)
+ return
+}
+
+func (p *parserMixin) Int32ListVar(target *[]int32) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newInt32Value(v.(*int32))
+ }))
+}
+
+// -- int64 Value
+type int64Value struct{ v *int64 }
+
+func newInt64Value(p *int64) *int64Value {
+ return &int64Value{p}
+}
+
+func (f *int64Value) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, 64)
+ if err == nil {
+ *f.v = (int64)(v)
+ }
+ return err
+}
+
+func (f *int64Value) Get() interface{} { return (int64)(*f.v) }
+
+func (f *int64Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Int64 parses the next command-line value as int64.
+func (p *parserMixin) Int64() (target *int64) {
+ target = new(int64)
+ p.Int64Var(target)
+ return
+}
+
+func (p *parserMixin) Int64Var(target *int64) {
+ p.SetValue(newInt64Value(target))
+}
+
+// Int64List accumulates int64 values into a slice.
+func (p *parserMixin) Int64List() (target *[]int64) {
+ target = new([]int64)
+ p.Int64ListVar(target)
+ return
+}
+
+func (p *parserMixin) Int64ListVar(target *[]int64) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newInt64Value(v.(*int64))
+ }))
+}
+
+// -- float64 Value
+type float64Value struct{ v *float64 }
+
+func newFloat64Value(p *float64) *float64Value {
+ return &float64Value{p}
+}
+
+func (f *float64Value) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 64)
+ if err == nil {
+ *f.v = (float64)(v)
+ }
+ return err
+}
+
+func (f *float64Value) Get() interface{} { return (float64)(*f.v) }
+
+func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Float64 parses the next command-line value as float64.
+func (p *parserMixin) Float64() (target *float64) {
+ target = new(float64)
+ p.Float64Var(target)
+ return
+}
+
+func (p *parserMixin) Float64Var(target *float64) {
+ p.SetValue(newFloat64Value(target))
+}
+
+// Float64List accumulates float64 values into a slice.
+func (p *parserMixin) Float64List() (target *[]float64) {
+ target = new([]float64)
+ p.Float64ListVar(target)
+ return
+}
+
+func (p *parserMixin) Float64ListVar(target *[]float64) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newFloat64Value(v.(*float64))
+ }))
+}
+
+// -- float32 Value
+type float32Value struct{ v *float32 }
+
+func newFloat32Value(p *float32) *float32Value {
+ return &float32Value{p}
+}
+
+func (f *float32Value) Set(s string) error {
+ v, err := strconv.ParseFloat(s, 32)
+ if err == nil {
+ *f.v = (float32)(v)
+ }
+ return err
+}
+
+func (f *float32Value) Get() interface{} { return (float32)(*f.v) }
+
+func (f *float32Value) String() string { return fmt.Sprintf("%v", *f) }
+
+// Float32 parses the next command-line value as float32.
+func (p *parserMixin) Float32() (target *float32) {
+ target = new(float32)
+ p.Float32Var(target)
+ return
+}
+
+func (p *parserMixin) Float32Var(target *float32) {
+ p.SetValue(newFloat32Value(target))
+}
+
+// Float32List accumulates float32 values into a slice.
+func (p *parserMixin) Float32List() (target *[]float32) {
+ target = new([]float32)
+ p.Float32ListVar(target)
+ return
+}
+
+func (p *parserMixin) Float32ListVar(target *[]float32) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newFloat32Value(v.(*float32))
+ }))
+}
+
+// DurationList accumulates time.Duration values into a slice.
+func (p *parserMixin) DurationList() (target *[]time.Duration) {
+ target = new([]time.Duration)
+ p.DurationListVar(target)
+ return
+}
+
+func (p *parserMixin) DurationListVar(target *[]time.Duration) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newDurationValue(v.(*time.Duration))
+ }))
+}
+
+// IPList accumulates net.IP values into a slice.
+func (p *parserMixin) IPList() (target *[]net.IP) {
+ target = new([]net.IP)
+ p.IPListVar(target)
+ return
+}
+
+func (p *parserMixin) IPListVar(target *[]net.IP) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newIPValue(v.(*net.IP))
+ }))
+}
+
+// TCPList accumulates *net.TCPAddr values into a slice.
+func (p *parserMixin) TCPList() (target *[]*net.TCPAddr) {
+ target = new([]*net.TCPAddr)
+ p.TCPListVar(target)
+ return
+}
+
+func (p *parserMixin) TCPListVar(target *[]*net.TCPAddr) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newTCPAddrValue(v.(**net.TCPAddr))
+ }))
+}
+
+// ExistingFiles accumulates string values into a slice.
+func (p *parserMixin) ExistingFiles() (target *[]string) {
+ target = new([]string)
+ p.ExistingFilesVar(target)
+ return
+}
+
+func (p *parserMixin) ExistingFilesVar(target *[]string) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newExistingFileValue(v.(*string))
+ }))
+}
+
+// ExistingDirs accumulates string values into a slice.
+func (p *parserMixin) ExistingDirs() (target *[]string) {
+ target = new([]string)
+ p.ExistingDirsVar(target)
+ return
+}
+
+func (p *parserMixin) ExistingDirsVar(target *[]string) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newExistingDirValue(v.(*string))
+ }))
+}
+
+// ExistingFilesOrDirs accumulates string values into a slice.
+func (p *parserMixin) ExistingFilesOrDirs() (target *[]string) {
+ target = new([]string)
+ p.ExistingFilesOrDirsVar(target)
+ return
+}
+
+func (p *parserMixin) ExistingFilesOrDirsVar(target *[]string) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newExistingFileOrDirValue(v.(*string))
+ }))
+}
+
+// -- *regexp.Regexp Value
+type regexpValue struct{ v **regexp.Regexp }
+
+func newRegexpValue(p **regexp.Regexp) *regexpValue {
+ return ®expValue{p}
+}
+
+func (f *regexpValue) Set(s string) error {
+ v, err := regexp.Compile(s)
+ if err == nil {
+ *f.v = (*regexp.Regexp)(v)
+ }
+ return err
+}
+
+func (f *regexpValue) Get() interface{} { return (*regexp.Regexp)(*f.v) }
+
+func (f *regexpValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Regexp parses the next command-line value as *regexp.Regexp.
+func (p *parserMixin) Regexp() (target **regexp.Regexp) {
+ target = new(*regexp.Regexp)
+ p.RegexpVar(target)
+ return
+}
+
+func (p *parserMixin) RegexpVar(target **regexp.Regexp) {
+ p.SetValue(newRegexpValue(target))
+}
+
+// RegexpList accumulates *regexp.Regexp values into a slice.
+func (p *parserMixin) RegexpList() (target *[]*regexp.Regexp) {
+ target = new([]*regexp.Regexp)
+ p.RegexpListVar(target)
+ return
+}
+
+func (p *parserMixin) RegexpListVar(target *[]*regexp.Regexp) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newRegexpValue(v.(**regexp.Regexp))
+ }))
+}
+
+// -- net.IP Value
+type resolvedIPValue struct{ v *net.IP }
+
+func newResolvedIPValue(p *net.IP) *resolvedIPValue {
+ return &resolvedIPValue{p}
+}
+
+func (f *resolvedIPValue) Set(s string) error {
+ v, err := resolveHost(s)
+ if err == nil {
+ *f.v = (net.IP)(v)
+ }
+ return err
+}
+
+func (f *resolvedIPValue) Get() interface{} { return (net.IP)(*f.v) }
+
+func (f *resolvedIPValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Resolve a hostname or IP to an IP.
+func (p *parserMixin) ResolvedIP() (target *net.IP) {
+ target = new(net.IP)
+ p.ResolvedIPVar(target)
+ return
+}
+
+func (p *parserMixin) ResolvedIPVar(target *net.IP) {
+ p.SetValue(newResolvedIPValue(target))
+}
+
+// ResolvedIPList accumulates net.IP values into a slice.
+func (p *parserMixin) ResolvedIPList() (target *[]net.IP) {
+ target = new([]net.IP)
+ p.ResolvedIPListVar(target)
+ return
+}
+
+func (p *parserMixin) ResolvedIPListVar(target *[]net.IP) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newResolvedIPValue(v.(*net.IP))
+ }))
+}
+
+// -- []byte Value
+type hexBytesValue struct{ v *[]byte }
+
+func newHexBytesValue(p *[]byte) *hexBytesValue {
+ return &hexBytesValue{p}
+}
+
+func (f *hexBytesValue) Set(s string) error {
+ v, err := hex.DecodeString(s)
+ if err == nil {
+ *f.v = ([]byte)(v)
+ }
+ return err
+}
+
+func (f *hexBytesValue) Get() interface{} { return ([]byte)(*f.v) }
+
+func (f *hexBytesValue) String() string { return fmt.Sprintf("%v", *f) }
+
+// Bytes as a hex string.
+func (p *parserMixin) HexBytes() (target *[]byte) {
+ target = new([]byte)
+ p.HexBytesVar(target)
+ return
+}
+
+func (p *parserMixin) HexBytesVar(target *[]byte) {
+ p.SetValue(newHexBytesValue(target))
+}
+
+// HexBytesList accumulates []byte values into a slice.
+func (p *parserMixin) HexBytesList() (target *[][]byte) {
+ target = new([][]byte)
+ p.HexBytesListVar(target)
+ return
+}
+
+func (p *parserMixin) HexBytesListVar(target *[][]byte) {
+ p.SetValue(newAccumulator(target, func(v interface{}) Value {
+ return newHexBytesValue(v.(*[]byte))
+ }))
+}
"revision": "4b6ea7319e214d98c938f12692336f7ca9348d6b",
"revisionTime": "2016-03-17T14:11:10Z"
},
+ {
+ "checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=",
+ "path": "github.com/alecthomas/template",
+ "revision": "a0175ee3bccc567396460bf5acd36800cb10c49c",
+ "revisionTime": "2016-04-05T07:15:01Z"
+ },
+ {
+ "checksumSHA1": "3wt0pTXXeS+S93unwhGoLIyGX/Q=",
+ "path": "github.com/alecthomas/template/parse",
+ "revision": "a0175ee3bccc567396460bf5acd36800cb10c49c",
+ "revisionTime": "2016-04-05T07:15:01Z"
+ },
+ {
+ "checksumSHA1": "fCc3grA7vIxfBru7R3SqjcW+oLI=",
+ "path": "github.com/alecthomas/units",
+ "revision": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a",
+ "revisionTime": "2015-10-22T06:55:26Z"
+ },
{
"checksumSHA1": "4QnLdmB1kG3N+KlDd1N+G9TWAGQ=",
"path": "github.com/beorn7/perks/quantile",
"revision": "cfdf022e86b4ecfb646e1efbd7db175dd623a8fa",
"revisionTime": "2017-06-28T09:51:01Z"
},
+ {
+ "checksumSHA1": "ZvPW/nSYs/UeoZejaV2ZSm9+2Do=",
+ "path": "gopkg.in/alecthomas/kingpin.v2",
+ "revision": "7f0871f2e17818990e4eed73f9b5c2f429501228",
+ "revisionTime": "2017-03-28T23:38:35Z"
+ },
{
"checksumSHA1": "+OgOXBoiQ+X+C2dsAeiOHwBIEH0=",
"path": "gopkg.in/yaml.v2",