forked from mirrors/statsd_exporter
Update vendor github.com/prometheus/common/...
This commit is contained in:
parent
fe0b1f55de
commit
115cb0e157
106 changed files with 20640 additions and 339 deletions
27
vendor/github.com/alecthomas/template/LICENSE
generated
vendored
Normal file
27
vendor/github.com/alecthomas/template/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
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.
|
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
Normal file
25
vendor/github.com/alecthomas/template/README.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# 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
|
||||||
|
```
|
406
vendor/github.com/alecthomas/template/doc.go
generated
vendored
Normal file
406
vendor/github.com/alecthomas/template/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
// 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
|
845
vendor/github.com/alecthomas/template/exec.go
generated
vendored
Normal file
845
vendor/github.com/alecthomas/template/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,845 @@
|
||||||
|
// 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
|
||||||
|
}
|
598
vendor/github.com/alecthomas/template/funcs.go
generated
vendored
Normal file
598
vendor/github.com/alecthomas/template/funcs.go
generated
vendored
Normal file
|
@ -0,0 +1,598 @@
|
||||||
|
// 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
|
||||||
|
}
|
108
vendor/github.com/alecthomas/template/helper.go
generated
vendored
Normal file
108
vendor/github.com/alecthomas/template/helper.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// 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...)
|
||||||
|
}
|
556
vendor/github.com/alecthomas/template/parse/lex.go
generated
vendored
Normal file
556
vendor/github.com/alecthomas/template/parse/lex.go
generated
vendored
Normal file
|
@ -0,0 +1,556 @@
|
||||||
|
// 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)
|
||||||
|
}
|
834
vendor/github.com/alecthomas/template/parse/node.go
generated
vendored
Normal file
834
vendor/github.com/alecthomas/template/parse/node.go
generated
vendored
Normal file
|
@ -0,0 +1,834 @@
|
||||||
|
// 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())
|
||||||
|
}
|
700
vendor/github.com/alecthomas/template/parse/parse.go
generated
vendored
Normal file
700
vendor/github.com/alecthomas/template/parse/parse.go
generated
vendored
Normal file
|
@ -0,0 +1,700 @@
|
||||||
|
// 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
|
||||||
|
}
|
218
vendor/github.com/alecthomas/template/template.go
generated
vendored
Normal file
218
vendor/github.com/alecthomas/template/template.go
generated
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
// 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
|
||||||
|
}
|
19
vendor/github.com/alecthomas/units/COPYING
generated
vendored
Normal file
19
vendor/github.com/alecthomas/units/COPYING
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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.
|
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
Normal file
11
vendor/github.com/alecthomas/units/README.md
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# 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
|
||||||
|
```
|
83
vendor/github.com/alecthomas/units/bytes.go
generated
vendored
Normal file
83
vendor/github.com/alecthomas/units/bytes.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
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
|
||||||
|
}
|
13
vendor/github.com/alecthomas/units/doc.go
generated
vendored
Normal file
13
vendor/github.com/alecthomas/units/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// 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
|
26
vendor/github.com/alecthomas/units/si.go
generated
vendored
Normal file
26
vendor/github.com/alecthomas/units/si.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
138
vendor/github.com/alecthomas/units/util.go
generated
vendored
Normal file
138
vendor/github.com/alecthomas/units/util.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
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
|
||||||
|
}
|
72
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
72
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
|
@ -31,6 +31,7 @@ type Decoder interface {
|
||||||
Decode(*dto.MetricFamily) error
|
Decode(*dto.MetricFamily) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeOptions contains options used by the Decoder and in sample extraction.
|
||||||
type DecodeOptions struct {
|
type DecodeOptions struct {
|
||||||
// Timestamp is added to each value from the stream that has no explicit timestamp set.
|
// Timestamp is added to each value from the stream that has no explicit timestamp set.
|
||||||
Timestamp model.Time
|
Timestamp model.Time
|
||||||
|
@ -46,10 +47,7 @@ func ResponseFormat(h http.Header) Format {
|
||||||
return FmtUnknown
|
return FmtUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const textType = "text/plain"
|
||||||
textType = "text/plain"
|
|
||||||
jsonType = "application/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
switch mediatype {
|
switch mediatype {
|
||||||
case ProtoType:
|
case ProtoType:
|
||||||
|
@ -66,22 +64,6 @@ func ResponseFormat(h http.Header) Format {
|
||||||
return FmtUnknown
|
return FmtUnknown
|
||||||
}
|
}
|
||||||
return FmtText
|
return FmtText
|
||||||
|
|
||||||
case jsonType:
|
|
||||||
var prometheusAPIVersion string
|
|
||||||
|
|
||||||
if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
|
|
||||||
prometheusAPIVersion = params["version"]
|
|
||||||
} else {
|
|
||||||
prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch prometheusAPIVersion {
|
|
||||||
case "0.0.2", "":
|
|
||||||
return fmtJSON2
|
|
||||||
default:
|
|
||||||
return FmtUnknown
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FmtUnknown
|
return FmtUnknown
|
||||||
|
@ -93,8 +75,6 @@ func NewDecoder(r io.Reader, format Format) Decoder {
|
||||||
switch format {
|
switch format {
|
||||||
case FmtProtoDelim:
|
case FmtProtoDelim:
|
||||||
return &protoDecoder{r: r}
|
return &protoDecoder{r: r}
|
||||||
case fmtJSON2:
|
|
||||||
return newJSON2Decoder(r)
|
|
||||||
}
|
}
|
||||||
return &textDecoder{r: r}
|
return &textDecoder{r: r}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +112,7 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// textDecoder implements the Decoder interface for the text protcol.
|
// textDecoder implements the Decoder interface for the text protocol.
|
||||||
type textDecoder struct {
|
type textDecoder struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
p TextParser
|
p TextParser
|
||||||
|
@ -163,6 +143,8 @@ func (d *textDecoder) Decode(v *dto.MetricFamily) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SampleDecoder wraps a Decoder to extract samples from the metric families
|
||||||
|
// decoded by the wrapped Decoder.
|
||||||
type SampleDecoder struct {
|
type SampleDecoder struct {
|
||||||
Dec Decoder
|
Dec Decoder
|
||||||
Opts *DecodeOptions
|
Opts *DecodeOptions
|
||||||
|
@ -170,37 +152,51 @@ type SampleDecoder struct {
|
||||||
f dto.MetricFamily
|
f dto.MetricFamily
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode calls the Decode method of the wrapped Decoder and then extracts the
|
||||||
|
// samples from the decoded MetricFamily into the provided model.Vector.
|
||||||
func (sd *SampleDecoder) Decode(s *model.Vector) error {
|
func (sd *SampleDecoder) Decode(s *model.Vector) error {
|
||||||
if err := sd.Dec.Decode(&sd.f); err != nil {
|
err := sd.Dec.Decode(&sd.f)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*s = extractSamples(&sd.f, sd.Opts)
|
*s, err = extractSamples(&sd.f, sd.Opts)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract samples builds a slice of samples from the provided metric families.
|
// ExtractSamples builds a slice of samples from the provided metric
|
||||||
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) model.Vector {
|
// families. If an error occurs during sample extraction, it continues to
|
||||||
var all model.Vector
|
// extract from the remaining metric families. The returned error is the last
|
||||||
|
// error that has occured.
|
||||||
|
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
|
||||||
|
var (
|
||||||
|
all model.Vector
|
||||||
|
lastErr error
|
||||||
|
)
|
||||||
for _, f := range fams {
|
for _, f := range fams {
|
||||||
all = append(all, extractSamples(f, o)...)
|
some, err := extractSamples(f, o)
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
all = append(all, some...)
|
||||||
}
|
}
|
||||||
return all
|
return all, lastErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractSamples(f *dto.MetricFamily, o *DecodeOptions) model.Vector {
|
func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
|
||||||
switch f.GetType() {
|
switch f.GetType() {
|
||||||
case dto.MetricType_COUNTER:
|
case dto.MetricType_COUNTER:
|
||||||
return extractCounter(o, f)
|
return extractCounter(o, f), nil
|
||||||
case dto.MetricType_GAUGE:
|
case dto.MetricType_GAUGE:
|
||||||
return extractGauge(o, f)
|
return extractGauge(o, f), nil
|
||||||
case dto.MetricType_SUMMARY:
|
case dto.MetricType_SUMMARY:
|
||||||
return extractSummary(o, f)
|
return extractSummary(o, f), nil
|
||||||
case dto.MetricType_UNTYPED:
|
case dto.MetricType_UNTYPED:
|
||||||
return extractUntyped(o, f)
|
return extractUntyped(o, f), nil
|
||||||
case dto.MetricType_HISTOGRAM:
|
case dto.MetricType_HISTOGRAM:
|
||||||
return extractHistogram(o, f)
|
return extractHistogram(o, f), nil
|
||||||
}
|
}
|
||||||
panic("expfmt.extractSamples: unknown metric family type")
|
return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||||
|
|
10
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
10
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
|
@ -11,14 +11,15 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// A package for reading and writing Prometheus metrics.
|
// Package expfmt contains tools for reading and writing Prometheus metrics.
|
||||||
package expfmt
|
package expfmt
|
||||||
|
|
||||||
|
// Format specifies the HTTP content type of the different wire protocols.
|
||||||
type Format string
|
type Format string
|
||||||
|
|
||||||
|
// Constants to assemble the Content-Type values for the different wire protocols.
|
||||||
const (
|
const (
|
||||||
TextVersion = "0.0.4"
|
TextVersion = "0.0.4"
|
||||||
|
|
||||||
ProtoType = `application/vnd.google.protobuf`
|
ProtoType = `application/vnd.google.protobuf`
|
||||||
ProtoProtocol = `io.prometheus.client.MetricFamily`
|
ProtoProtocol = `io.prometheus.client.MetricFamily`
|
||||||
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
|
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
|
||||||
|
@ -29,9 +30,6 @@ const (
|
||||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||||
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
||||||
|
|
||||||
// fmtJSON2 is hidden as it is deprecated.
|
|
||||||
fmtJSON2 Format = `application/json; version=0.0.2`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
4
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
4
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
|
@ -20,8 +20,8 @@ import "bytes"
|
||||||
|
|
||||||
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
|
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
|
||||||
//
|
//
|
||||||
// go-fuzz-build github.com/prometheus/client_golang/text
|
// go-fuzz-build github.com/prometheus/common/expfmt
|
||||||
// go-fuzz -bin text-fuzz.zip -workdir fuzz
|
// go-fuzz -bin expfmt-fuzz.zip -workdir fuzz
|
||||||
//
|
//
|
||||||
// Further input samples should go in the folder fuzz/corpus.
|
// Further input samples should go in the folder fuzz/corpus.
|
||||||
func Fuzz(in []byte) int {
|
func Fuzz(in []byte) int {
|
||||||
|
|
174
vendor/github.com/prometheus/common/expfmt/json_decode.go
generated
vendored
174
vendor/github.com/prometheus/common/expfmt/json_decode.go
generated
vendored
|
@ -1,174 +0,0 @@
|
||||||
// Copyright 2015 The Prometheus Authors
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package expfmt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
|
||||||
|
|
||||||
"github.com/prometheus/common/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type json2Decoder struct {
|
|
||||||
dec *json.Decoder
|
|
||||||
fams []*dto.MetricFamily
|
|
||||||
}
|
|
||||||
|
|
||||||
func newJSON2Decoder(r io.Reader) Decoder {
|
|
||||||
return &json2Decoder{
|
|
||||||
dec: json.NewDecoder(r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type histogram002 struct {
|
|
||||||
Labels model.LabelSet `json:"labels"`
|
|
||||||
Values map[string]float64 `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type counter002 struct {
|
|
||||||
Labels model.LabelSet `json:"labels"`
|
|
||||||
Value float64 `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func protoLabelSet(base, ext model.LabelSet) ([]*dto.LabelPair, error) {
|
|
||||||
labels := base.Clone().Merge(ext)
|
|
||||||
delete(labels, model.MetricNameLabel)
|
|
||||||
|
|
||||||
names := make([]string, 0, len(labels))
|
|
||||||
for ln := range labels {
|
|
||||||
names = append(names, string(ln))
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
|
|
||||||
pairs := make([]*dto.LabelPair, 0, len(labels))
|
|
||||||
|
|
||||||
for _, ln := range names {
|
|
||||||
if !model.LabelNameRE.MatchString(ln) {
|
|
||||||
return nil, fmt.Errorf("invalid label name %q", ln)
|
|
||||||
}
|
|
||||||
lv := labels[model.LabelName(ln)]
|
|
||||||
|
|
||||||
pairs = append(pairs, &dto.LabelPair{
|
|
||||||
Name: proto.String(ln),
|
|
||||||
Value: proto.String(string(lv)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return pairs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *json2Decoder) more() error {
|
|
||||||
var entities []struct {
|
|
||||||
BaseLabels model.LabelSet `json:"baseLabels"`
|
|
||||||
Docstring string `json:"docstring"`
|
|
||||||
Metric struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Values json.RawMessage `json:"value"`
|
|
||||||
} `json:"metric"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.dec.Decode(&entities); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, e := range entities {
|
|
||||||
f := &dto.MetricFamily{
|
|
||||||
Name: proto.String(string(e.BaseLabels[model.MetricNameLabel])),
|
|
||||||
Help: proto.String(e.Docstring),
|
|
||||||
Type: dto.MetricType_UNTYPED.Enum(),
|
|
||||||
Metric: []*dto.Metric{},
|
|
||||||
}
|
|
||||||
|
|
||||||
d.fams = append(d.fams, f)
|
|
||||||
|
|
||||||
switch e.Metric.Type {
|
|
||||||
case "counter", "gauge":
|
|
||||||
var values []counter002
|
|
||||||
|
|
||||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
|
||||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ctr := range values {
|
|
||||||
labels, err := protoLabelSet(e.BaseLabels, ctr.Labels)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.Metric = append(f.Metric, &dto.Metric{
|
|
||||||
Label: labels,
|
|
||||||
Untyped: &dto.Untyped{
|
|
||||||
Value: proto.Float64(ctr.Value),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "histogram":
|
|
||||||
var values []histogram002
|
|
||||||
|
|
||||||
if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
|
|
||||||
return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hist := range values {
|
|
||||||
quants := make([]string, 0, len(values))
|
|
||||||
for q := range hist.Values {
|
|
||||||
quants = append(quants, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(quants)
|
|
||||||
|
|
||||||
for _, q := range quants {
|
|
||||||
value := hist.Values[q]
|
|
||||||
// The correct label is "quantile" but to not break old expressions
|
|
||||||
// this remains "percentile"
|
|
||||||
hist.Labels["percentile"] = model.LabelValue(q)
|
|
||||||
|
|
||||||
labels, err := protoLabelSet(e.BaseLabels, hist.Labels)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Metric = append(f.Metric, &dto.Metric{
|
|
||||||
Label: labels,
|
|
||||||
Untyped: &dto.Untyped{
|
|
||||||
Value: proto.Float64(value),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown metric type %q", e.Metric.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode implements the Decoder interface.
|
|
||||||
func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
|
|
||||||
if len(d.fams) == 0 {
|
|
||||||
if err := d.more(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*v = *d.fams[0]
|
|
||||||
d.fams = d.fams[1:]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
30
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
30
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
|
@ -14,7 +14,6 @@
|
||||||
package expfmt
|
package expfmt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
@ -26,9 +25,12 @@ import (
|
||||||
|
|
||||||
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
||||||
// writes the resulting lines to 'out'. It returns the number of bytes written
|
// writes the resulting lines to 'out'. It returns the number of bytes written
|
||||||
// and any error encountered. This function does not perform checks on the
|
// and any error encountered. The output will have the same order as the input,
|
||||||
// content of the metric and label names, i.e. invalid metric or label names
|
// no further sorting is performed. Furthermore, this function assumes the input
|
||||||
|
// is already sanitized and does not perform any sanity checks. If the input
|
||||||
|
// contains duplicate metrics or invalid metric or label names, the conversion
|
||||||
// will result in invalid text format output.
|
// will result in invalid text format output.
|
||||||
|
//
|
||||||
// This method fulfills the type 'prometheus.encoder'.
|
// This method fulfills the type 'prometheus.encoder'.
|
||||||
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
var written int
|
var written int
|
||||||
|
@ -285,21 +287,17 @@ func labelPairsToText(
|
||||||
return written, nil
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
||||||
|
escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
||||||
|
)
|
||||||
|
|
||||||
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
||||||
// includeDoubleQuote is true - '"' by '\"'.
|
// includeDoubleQuote is true - '"' by '\"'.
|
||||||
func escapeString(v string, includeDoubleQuote bool) string {
|
func escapeString(v string, includeDoubleQuote bool) string {
|
||||||
result := bytes.NewBuffer(make([]byte, 0, len(v)))
|
if includeDoubleQuote {
|
||||||
for _, c := range v {
|
return escapeWithDoubleQuote.Replace(v)
|
||||||
switch {
|
|
||||||
case c == '\\':
|
|
||||||
result.WriteString(`\\`)
|
|
||||||
case includeDoubleQuote && c == '"':
|
|
||||||
result.WriteString(`\"`)
|
|
||||||
case c == '\n':
|
|
||||||
result.WriteString(`\n`)
|
|
||||||
default:
|
|
||||||
result.WriteRune(c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result.String()
|
|
||||||
|
return escape.Replace(v)
|
||||||
}
|
}
|
||||||
|
|
6
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
6
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
|
@ -47,7 +47,7 @@ func (e ParseError) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextParser is used to parse the simple and flat text-based exchange format. Its
|
// TextParser is used to parse the simple and flat text-based exchange format. Its
|
||||||
// nil value is ready to use.
|
// zero value is ready to use.
|
||||||
type TextParser struct {
|
type TextParser struct {
|
||||||
metricFamiliesByName map[string]*dto.MetricFamily
|
metricFamiliesByName map[string]*dto.MetricFamily
|
||||||
buf *bufio.Reader // Where the parsed input is read through.
|
buf *bufio.Reader // Where the parsed input is read through.
|
||||||
|
@ -315,6 +315,10 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||||
if p.readTokenAsLabelValue(); p.err != nil {
|
if p.readTokenAsLabelValue(); p.err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if !model.LabelValue(p.currentToken.String()).IsValid() {
|
||||||
|
p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
p.currentLabelPair.Value = proto.String(p.currentToken.String())
|
p.currentLabelPair.Value = proto.String(p.currentToken.String())
|
||||||
// Special treatment of summaries:
|
// Special treatment of summaries:
|
||||||
// - Quantile labels are special, will result in dto.Quantile later.
|
// - Quantile labels are special, will result in dto.Quantile later.
|
||||||
|
|
89
vendor/github.com/prometheus/common/log/eventlog_formatter.go
generated
vendored
Normal file
89
vendor/github.com/prometheus/common/log/eventlog_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2015 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
setEventlogFormatter = func(l logger, name string, debugAsInfo bool) error {
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("missing name parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmter, err := newEventlogger(name, debugAsInfo, l.entry.Logger.Formatter)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error creating eventlog formatter: %v\n", err)
|
||||||
|
l.Errorf("can't connect logger to eventlog: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.entry.Logger.Formatter = fmter
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventlogger struct {
|
||||||
|
log *eventlog.Log
|
||||||
|
debugAsInfo bool
|
||||||
|
wrap logrus.Formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEventlogger(name string, debugAsInfo bool, fmter logrus.Formatter) (*eventlogger, error) {
|
||||||
|
logHandle, err := eventlog.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &eventlogger{log: logHandle, debugAsInfo: debugAsInfo, wrap: fmter}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *eventlogger) Format(e *logrus.Entry) ([]byte, error) {
|
||||||
|
data, err := s.wrap.Format(e)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "eventlogger: can't format entry: %v\n", err)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Level {
|
||||||
|
case logrus.PanicLevel:
|
||||||
|
fallthrough
|
||||||
|
case logrus.FatalLevel:
|
||||||
|
fallthrough
|
||||||
|
case logrus.ErrorLevel:
|
||||||
|
err = s.log.Error(102, e.Message)
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
err = s.log.Warning(101, e.Message)
|
||||||
|
case logrus.InfoLevel:
|
||||||
|
err = s.log.Info(100, e.Message)
|
||||||
|
case logrus.DebugLevel:
|
||||||
|
if s.debugAsInfo {
|
||||||
|
err = s.log.Info(100, e.Message)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = s.log.Info(100, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "eventlogger: can't send log to eventlog: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, err
|
||||||
|
}
|
183
vendor/github.com/prometheus/common/log/log.go
generated
vendored
183
vendor/github.com/prometheus/common/log/log.go
generated
vendored
|
@ -14,88 +14,59 @@
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type levelFlag struct{}
|
|
||||||
|
|
||||||
// String implements flag.Value.
|
|
||||||
func (f levelFlag) String() string {
|
|
||||||
return origLogger.Level.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements flag.Value.
|
|
||||||
func (f levelFlag) Set(level string) error {
|
|
||||||
l, err := logrus.ParseLevel(level)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
origLogger.Level = l
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setSyslogFormatter is nil if the target architecture does not support syslog.
|
// setSyslogFormatter is nil if the target architecture does not support syslog.
|
||||||
var setSyslogFormatter func(string, string) error
|
var setSyslogFormatter func(logger, string, string) error
|
||||||
|
|
||||||
|
// setEventlogFormatter is nil if the target OS does not support Eventlog (i.e., is not Windows).
|
||||||
|
var setEventlogFormatter func(logger, string, bool) error
|
||||||
|
|
||||||
func setJSONFormatter() {
|
func setJSONFormatter() {
|
||||||
origLogger.Formatter = &logrus.JSONFormatter{}
|
origLogger.Formatter = &logrus.JSONFormatter{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type logFormatFlag struct{ uri string }
|
type loggerSettings struct {
|
||||||
|
level string
|
||||||
// String implements flag.Value.
|
format string
|
||||||
func (f logFormatFlag) String() string {
|
|
||||||
return f.uri
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set implements flag.Value.
|
func (s *loggerSettings) apply(ctx *kingpin.ParseContext) error {
|
||||||
func (f logFormatFlag) Set(format string) error {
|
err := baseLogger.SetLevel(s.level)
|
||||||
f.uri = format
|
|
||||||
u, err := url.Parse(format)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if u.Scheme != "logger" {
|
err = baseLogger.SetFormat(s.format)
|
||||||
return fmt.Errorf("invalid scheme %s", u.Scheme)
|
return err
|
||||||
}
|
|
||||||
jsonq := u.Query().Get("json")
|
|
||||||
if jsonq == "true" {
|
|
||||||
setJSONFormatter()
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Opaque {
|
|
||||||
case "syslog":
|
|
||||||
if setSyslogFormatter == nil {
|
|
||||||
return fmt.Errorf("system does not support syslog")
|
|
||||||
}
|
|
||||||
appname := u.Query().Get("appname")
|
|
||||||
facility := u.Query().Get("local")
|
|
||||||
return setSyslogFormatter(appname, facility)
|
|
||||||
case "stdout":
|
|
||||||
origLogger.Out = os.Stdout
|
|
||||||
case "stderr":
|
|
||||||
origLogger.Out = os.Stderr
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported logger %s", u.Opaque)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
// AddFlags adds the flags used by this package to the Kingpin application.
|
||||||
// In order for these flags to take effect, the user of the package must call
|
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
|
||||||
// flag.Parse() before logging anything.
|
func AddFlags(a *kingpin.Application) {
|
||||||
flag.Var(levelFlag{}, "log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal].")
|
s := loggerSettings{}
|
||||||
flag.Var(logFormatFlag{}, "log.format", "If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr.")
|
kingpin.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]").
|
||||||
|
Default(origLogger.Level.String()).
|
||||||
|
StringVar(&s.level)
|
||||||
|
defaultFormat := url.URL{Scheme: "logger", Opaque: "stderr"}
|
||||||
|
kingpin.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`).
|
||||||
|
Default(defaultFormat.String()).
|
||||||
|
StringVar(&s.format)
|
||||||
|
a.Action(s.apply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger is the interface for loggers used in the Prometheus components.
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Debug(...interface{})
|
Debug(...interface{})
|
||||||
Debugln(...interface{})
|
Debugln(...interface{})
|
||||||
|
@ -118,6 +89,9 @@ type Logger interface {
|
||||||
Fatalf(string, ...interface{})
|
Fatalf(string, ...interface{})
|
||||||
|
|
||||||
With(key string, value interface{}) Logger
|
With(key string, value interface{}) Logger
|
||||||
|
|
||||||
|
SetFormat(string) error
|
||||||
|
SetLevel(string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type logger struct {
|
type logger struct {
|
||||||
|
@ -203,6 +177,58 @@ func (l logger) Fatalf(format string, args ...interface{}) {
|
||||||
l.sourced().Fatalf(format, args...)
|
l.sourced().Fatalf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l logger) SetLevel(level string) error {
|
||||||
|
lvl, err := logrus.ParseLevel(level)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.entry.Logger.Level = lvl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l logger) SetFormat(format string) error {
|
||||||
|
u, err := url.Parse(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if u.Scheme != "logger" {
|
||||||
|
return fmt.Errorf("invalid scheme %s", u.Scheme)
|
||||||
|
}
|
||||||
|
jsonq := u.Query().Get("json")
|
||||||
|
if jsonq == "true" {
|
||||||
|
setJSONFormatter()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Opaque {
|
||||||
|
case "syslog":
|
||||||
|
if setSyslogFormatter == nil {
|
||||||
|
return fmt.Errorf("system does not support syslog")
|
||||||
|
}
|
||||||
|
appname := u.Query().Get("appname")
|
||||||
|
facility := u.Query().Get("local")
|
||||||
|
return setSyslogFormatter(l, appname, facility)
|
||||||
|
case "eventlog":
|
||||||
|
if setEventlogFormatter == nil {
|
||||||
|
return fmt.Errorf("system does not support eventlog")
|
||||||
|
}
|
||||||
|
name := u.Query().Get("name")
|
||||||
|
debugAsInfo := false
|
||||||
|
debugAsInfoRaw := u.Query().Get("debugAsInfo")
|
||||||
|
if parsedDebugAsInfo, err := strconv.ParseBool(debugAsInfoRaw); err == nil {
|
||||||
|
debugAsInfo = parsedDebugAsInfo
|
||||||
|
}
|
||||||
|
return setEventlogFormatter(l, name, debugAsInfo)
|
||||||
|
case "stdout":
|
||||||
|
l.entry.Logger.Out = os.Stdout
|
||||||
|
case "stderr":
|
||||||
|
l.entry.Logger.Out = os.Stderr
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported logger %q", u.Opaque)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// sourced adds a source field to the logger that contains
|
// sourced adds a source field to the logger that contains
|
||||||
// the file name and line where the logging happened.
|
// the file name and line where the logging happened.
|
||||||
func (l logger) sourced() *logrus.Entry {
|
func (l logger) sourced() *logrus.Entry {
|
||||||
|
@ -220,10 +246,26 @@ func (l logger) sourced() *logrus.Entry {
|
||||||
var origLogger = logrus.New()
|
var origLogger = logrus.New()
|
||||||
var baseLogger = logger{entry: logrus.NewEntry(origLogger)}
|
var baseLogger = logger{entry: logrus.NewEntry(origLogger)}
|
||||||
|
|
||||||
|
// Base returns the default Logger logging to
|
||||||
func Base() Logger {
|
func Base() Logger {
|
||||||
return baseLogger
|
return baseLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLogger returns a new Logger logging to out.
|
||||||
|
func NewLogger(w io.Writer) Logger {
|
||||||
|
l := logrus.New()
|
||||||
|
l.Out = w
|
||||||
|
return logger{entry: logrus.NewEntry(l)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNopLogger returns a logger that discards all log messages.
|
||||||
|
func NewNopLogger() Logger {
|
||||||
|
l := logrus.New()
|
||||||
|
l.Out = ioutil.Discard
|
||||||
|
return logger{entry: logrus.NewEntry(l)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With adds a field to the logger.
|
||||||
func With(key string, value interface{}) Logger {
|
func With(key string, value interface{}) Logger {
|
||||||
return baseLogger.With(key, value)
|
return baseLogger.With(key, value)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +275,7 @@ func Debug(args ...interface{}) {
|
||||||
baseLogger.sourced().Debug(args...)
|
baseLogger.sourced().Debug(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug logs a message at level Debug on the standard logger.
|
// Debugln logs a message at level Debug on the standard logger.
|
||||||
func Debugln(args ...interface{}) {
|
func Debugln(args ...interface{}) {
|
||||||
baseLogger.sourced().Debugln(args...)
|
baseLogger.sourced().Debugln(args...)
|
||||||
}
|
}
|
||||||
|
@ -248,7 +290,7 @@ func Info(args ...interface{}) {
|
||||||
baseLogger.sourced().Info(args...)
|
baseLogger.sourced().Info(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info logs a message at level Info on the standard logger.
|
// Infoln logs a message at level Info on the standard logger.
|
||||||
func Infoln(args ...interface{}) {
|
func Infoln(args ...interface{}) {
|
||||||
baseLogger.sourced().Infoln(args...)
|
baseLogger.sourced().Infoln(args...)
|
||||||
}
|
}
|
||||||
|
@ -263,7 +305,7 @@ func Warn(args ...interface{}) {
|
||||||
baseLogger.sourced().Warn(args...)
|
baseLogger.sourced().Warn(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn logs a message at level Warn on the standard logger.
|
// Warnln logs a message at level Warn on the standard logger.
|
||||||
func Warnln(args ...interface{}) {
|
func Warnln(args ...interface{}) {
|
||||||
baseLogger.sourced().Warnln(args...)
|
baseLogger.sourced().Warnln(args...)
|
||||||
}
|
}
|
||||||
|
@ -278,7 +320,7 @@ func Error(args ...interface{}) {
|
||||||
baseLogger.sourced().Error(args...)
|
baseLogger.sourced().Error(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error logs a message at level Error on the standard logger.
|
// Errorln logs a message at level Error on the standard logger.
|
||||||
func Errorln(args ...interface{}) {
|
func Errorln(args ...interface{}) {
|
||||||
baseLogger.sourced().Errorln(args...)
|
baseLogger.sourced().Errorln(args...)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +335,7 @@ func Fatal(args ...interface{}) {
|
||||||
baseLogger.sourced().Fatal(args...)
|
baseLogger.sourced().Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal logs a message at level Fatal on the standard logger.
|
// Fatalln logs a message at level Fatal on the standard logger.
|
||||||
func Fatalln(args ...interface{}) {
|
func Fatalln(args ...interface{}) {
|
||||||
baseLogger.sourced().Fatalln(args...)
|
baseLogger.sourced().Fatalln(args...)
|
||||||
}
|
}
|
||||||
|
@ -302,3 +344,16 @@ func Fatalln(args ...interface{}) {
|
||||||
func Fatalf(format string, args ...interface{}) {
|
func Fatalf(format string, args ...interface{}) {
|
||||||
baseLogger.sourced().Fatalf(format, args...)
|
baseLogger.sourced().Fatalf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errorLogWriter struct{}
|
||||||
|
|
||||||
|
func (errorLogWriter) Write(b []byte) (int, error) {
|
||||||
|
baseLogger.sourced().Error(string(b))
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrorLogger returns a log.Logger that is meant to be used
|
||||||
|
// in the ErrorLog field of an http.Server to log HTTP server errors.
|
||||||
|
func NewErrorLogger() *log.Logger {
|
||||||
|
return log.New(&errorLogWriter{}, "", 0)
|
||||||
|
}
|
||||||
|
|
21
vendor/github.com/prometheus/common/log/syslog_formatter.go
generated
vendored
21
vendor/github.com/prometheus/common/log/syslog_formatter.go
generated
vendored
|
@ -20,11 +20,13 @@ import (
|
||||||
"log/syslog"
|
"log/syslog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ logrus.Formatter = (*syslogger)(nil)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
setSyslogFormatter = func(appname, local string) error {
|
setSyslogFormatter = func(l logger, appname, local string) error {
|
||||||
if appname == "" {
|
if appname == "" {
|
||||||
return fmt.Errorf("missing appname parameter")
|
return fmt.Errorf("missing appname parameter")
|
||||||
}
|
}
|
||||||
|
@ -32,18 +34,18 @@ func init() {
|
||||||
return fmt.Errorf("missing local parameter")
|
return fmt.Errorf("missing local parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmter, err := newSyslogger(appname, local, origLogger.Formatter)
|
fmter, err := newSyslogger(appname, local, l.entry.Logger.Formatter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "error creating syslog formatter: %v\n", err)
|
fmt.Fprintf(os.Stderr, "error creating syslog formatter: %v\n", err)
|
||||||
origLogger.Errorf("can't connect logger to syslog: %v", err)
|
l.entry.Errorf("can't connect logger to syslog: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
origLogger.Formatter = fmter
|
l.entry.Logger.Formatter = fmter
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ceeTag = []byte("@cee:")
|
var prefixTag []byte
|
||||||
|
|
||||||
type syslogger struct {
|
type syslogger struct {
|
||||||
wrap logrus.Formatter
|
wrap logrus.Formatter
|
||||||
|
@ -56,6 +58,11 @@ func newSyslogger(appname string, facility string, fmter logrus.Formatter) (*sys
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out, err := syslog.New(priority, appname)
|
out, err := syslog.New(priority, appname)
|
||||||
|
_, isJSON := fmter.(*logrus.JSONFormatter)
|
||||||
|
if isJSON {
|
||||||
|
// add cee tag to json formatted syslogs
|
||||||
|
prefixTag = []byte("@cee:")
|
||||||
|
}
|
||||||
return &syslogger{
|
return &syslogger{
|
||||||
out: out,
|
out: out,
|
||||||
wrap: fmter,
|
wrap: fmter,
|
||||||
|
@ -92,7 +99,7 @@ func (s *syslogger) Format(e *logrus.Entry) ([]byte, error) {
|
||||||
}
|
}
|
||||||
// only append tag to data sent to syslog (line), not to what
|
// only append tag to data sent to syslog (line), not to what
|
||||||
// is returned
|
// is returned
|
||||||
line := string(append(ceeTag, data...))
|
line := string(append(prefixTag, data...))
|
||||||
|
|
||||||
switch e.Level {
|
switch e.Level {
|
||||||
case logrus.PanicLevel:
|
case logrus.PanicLevel:
|
||||||
|
|
12
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
12
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
|
@ -80,14 +80,18 @@ const (
|
||||||
QuantileLabel = "quantile"
|
QuantileLabel = "quantile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LabelNameRE is a regular expression matching valid label names.
|
// LabelNameRE is a regular expression matching valid label names. Note that the
|
||||||
|
// IsValid method of LabelName performs the same check but faster than a match
|
||||||
|
// with this regular expression.
|
||||||
var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||||
|
|
||||||
// A LabelName is a key for a LabelSet or Metric. It has a value associated
|
// A LabelName is a key for a LabelSet or Metric. It has a value associated
|
||||||
// therewith.
|
// therewith.
|
||||||
type LabelName string
|
type LabelName string
|
||||||
|
|
||||||
// IsValid is true iff the label name matches the pattern of LabelNameRE.
|
// IsValid is true iff the label name matches the pattern of LabelNameRE. This
|
||||||
|
// method, however, does not use LabelNameRE for the check but a much faster
|
||||||
|
// hardcoded implementation.
|
||||||
func (ln LabelName) IsValid() bool {
|
func (ln LabelName) IsValid() bool {
|
||||||
if len(ln) == 0 {
|
if len(ln) == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -106,7 +110,7 @@ func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
if err := unmarshal(&s); err != nil {
|
if err := unmarshal(&s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !LabelNameRE.MatchString(s) {
|
if !LabelName(s).IsValid() {
|
||||||
return fmt.Errorf("%q is not a valid label name", s)
|
return fmt.Errorf("%q is not a valid label name", s)
|
||||||
}
|
}
|
||||||
*ln = LabelName(s)
|
*ln = LabelName(s)
|
||||||
|
@ -119,7 +123,7 @@ func (ln *LabelName) UnmarshalJSON(b []byte) error {
|
||||||
if err := json.Unmarshal(b, &s); err != nil {
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !LabelNameRE.MatchString(s) {
|
if !LabelName(s).IsValid() {
|
||||||
return fmt.Errorf("%q is not a valid label name", s)
|
return fmt.Errorf("%q is not a valid label name", s)
|
||||||
}
|
}
|
||||||
*ln = LabelName(s)
|
*ln = LabelName(s)
|
||||||
|
|
2
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
2
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
|
@ -160,7 +160,7 @@ func (l *LabelSet) UnmarshalJSON(b []byte) error {
|
||||||
// LabelName as a string and does not call its UnmarshalJSON method.
|
// LabelName as a string and does not call its UnmarshalJSON method.
|
||||||
// Thus, we have to replicate the behavior here.
|
// Thus, we have to replicate the behavior here.
|
||||||
for ln := range m {
|
for ln := range m {
|
||||||
if !LabelNameRE.MatchString(string(ln)) {
|
if !ln.IsValid() {
|
||||||
return fmt.Errorf("%q is not a valid label name", ln)
|
return fmt.Errorf("%q is not a valid label name", ln)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
11
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
|
@ -21,8 +21,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
separator = []byte{0}
|
separator = []byte{0}
|
||||||
MetricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
|
// MetricNameRE is a regular expression matching valid metric
|
||||||
|
// names. Note that the IsValidMetricName function performs the same
|
||||||
|
// check but faster than a match with this regular expression.
|
||||||
|
MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
|
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
|
||||||
|
@ -41,7 +44,7 @@ func (m Metric) Before(o Metric) bool {
|
||||||
|
|
||||||
// Clone returns a copy of the Metric.
|
// Clone returns a copy of the Metric.
|
||||||
func (m Metric) Clone() Metric {
|
func (m Metric) Clone() Metric {
|
||||||
clone := Metric{}
|
clone := make(Metric, len(m))
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
clone[k] = v
|
clone[k] = v
|
||||||
}
|
}
|
||||||
|
@ -85,6 +88,8 @@ func (m Metric) FastFingerprint() Fingerprint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
|
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
|
||||||
|
// This function, however, does not use MetricNameRE for the check but a much
|
||||||
|
// faster hardcoded implementation.
|
||||||
func IsValidMetricName(n LabelValue) bool {
|
func IsValidMetricName(n LabelValue) bool {
|
||||||
if len(n) == 0 {
|
if len(n) == 0 {
|
||||||
return false
|
return false
|
||||||
|
|
2
vendor/github.com/prometheus/common/model/model.go
generated
vendored
2
vendor/github.com/prometheus/common/model/model.go
generated
vendored
|
@ -12,5 +12,5 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package model contains common data structures that are shared across
|
// Package model contains common data structures that are shared across
|
||||||
// Prometheus componenets and libraries.
|
// Prometheus components and libraries.
|
||||||
package model
|
package model
|
||||||
|
|
14
vendor/github.com/prometheus/common/model/time.go
generated
vendored
14
vendor/github.com/prometheus/common/model/time.go
generated
vendored
|
@ -163,9 +163,21 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
||||||
// This type should not propagate beyond the scope of input/output processing.
|
// This type should not propagate beyond the scope of input/output processing.
|
||||||
type Duration time.Duration
|
type Duration time.Duration
|
||||||
|
|
||||||
|
// Set implements pflag/flag.Value
|
||||||
|
func (d *Duration) Set(s string) error {
|
||||||
|
var err error
|
||||||
|
*d, err = ParseDuration(s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements pflag.Value
|
||||||
|
func (d *Duration) Type() string {
|
||||||
|
return "duration"
|
||||||
|
}
|
||||||
|
|
||||||
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
||||||
|
|
||||||
// StringToDuration parses a string into a time.Duration, assuming that a year
|
// ParseDuration parses a string into a time.Duration, assuming that a year
|
||||||
// always has 365d, a week always has 7d, and a day always has 24h.
|
// always has 365d, a week always has 7d, and a day always has 24h.
|
||||||
func ParseDuration(durationStr string) (Duration, error) {
|
func ParseDuration(durationStr string) (Duration, error) {
|
||||||
matches := durationRE.FindStringSubmatch(durationStr)
|
matches := durationRE.FindStringSubmatch(durationStr)
|
||||||
|
|
37
vendor/github.com/prometheus/common/model/value.go
generated
vendored
37
vendor/github.com/prometheus/common/model/value.go
generated
vendored
|
@ -16,11 +16,28 @@ package model
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
|
||||||
|
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
|
||||||
|
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
|
||||||
|
// of 0, which is possible to appear in a real SamplePair and thus not
|
||||||
|
// suitable to signal a non-existing SamplePair.
|
||||||
|
ZeroSamplePair = SamplePair{Timestamp: Earliest}
|
||||||
|
|
||||||
|
// ZeroSample is the pseudo zero-value of Sample used to signal a
|
||||||
|
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
|
||||||
|
// and metric nil. Note that the natural zero value of Sample has a timestamp
|
||||||
|
// of 0, which is possible to appear in a real Sample and thus not suitable
|
||||||
|
// to signal a non-existing Sample.
|
||||||
|
ZeroSample = Sample{Timestamp: Earliest}
|
||||||
|
)
|
||||||
|
|
||||||
// A SampleValue is a representation of a value for a given sample at a given
|
// A SampleValue is a representation of a value for a given sample at a given
|
||||||
// time.
|
// time.
|
||||||
type SampleValue float64
|
type SampleValue float64
|
||||||
|
@ -43,8 +60,14 @@ func (v *SampleValue) UnmarshalJSON(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the value of v and o is equal or if both are NaN. Note
|
||||||
|
// that v==o is false if both are NaN. If you want the conventional float
|
||||||
|
// behavior, use == to compare two SampleValues.
|
||||||
func (v SampleValue) Equal(o SampleValue) bool {
|
func (v SampleValue) Equal(o SampleValue) bool {
|
||||||
return v == o
|
if v == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v SampleValue) String() string {
|
func (v SampleValue) String() string {
|
||||||
|
@ -77,9 +100,9 @@ func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns true if this SamplePair and o have equal Values and equal
|
// Equal returns true if this SamplePair and o have equal Values and equal
|
||||||
// Timestamps.
|
// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
|
||||||
func (s *SamplePair) Equal(o *SamplePair) bool {
|
func (s *SamplePair) Equal(o *SamplePair) bool {
|
||||||
return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
|
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SamplePair) String() string {
|
func (s SamplePair) String() string {
|
||||||
|
@ -93,7 +116,8 @@ type Sample struct {
|
||||||
Timestamp Time `json:"timestamp"`
|
Timestamp Time `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal compares first the metrics, then the timestamp, then the value.
|
// Equal compares first the metrics, then the timestamp, then the value. The
|
||||||
|
// sematics of value equality is defined by SampleValue.Equal.
|
||||||
func (s *Sample) Equal(o *Sample) bool {
|
func (s *Sample) Equal(o *Sample) bool {
|
||||||
if s == o {
|
if s == o {
|
||||||
return true
|
return true
|
||||||
|
@ -105,11 +129,8 @@ func (s *Sample) Equal(o *Sample) bool {
|
||||||
if !s.Timestamp.Equal(o.Timestamp) {
|
if !s.Timestamp.Equal(o.Timestamp) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s.Value != o.Value {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return s.Value.Equal(o.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Sample) String() string {
|
func (s Sample) String() string {
|
||||||
|
|
113
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
113
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
# 1.0.3
|
||||||
|
|
||||||
|
* Replace example files with testable examples
|
||||||
|
|
||||||
|
# 1.0.2
|
||||||
|
|
||||||
|
* bug: quote non-string values in text formatter (#583)
|
||||||
|
* Make (*Logger) SetLevel a public method
|
||||||
|
|
||||||
|
# 1.0.1
|
||||||
|
|
||||||
|
* bug: fix escaping in text formatter (#575)
|
||||||
|
|
||||||
|
# 1.0.0
|
||||||
|
|
||||||
|
* Officially changed name to lower-case
|
||||||
|
* bug: colors on Windows 10 (#541)
|
||||||
|
* bug: fix race in accessing level (#512)
|
||||||
|
|
||||||
|
# 0.11.5
|
||||||
|
|
||||||
|
* feature: add writer and writerlevel to entry (#372)
|
||||||
|
|
||||||
|
# 0.11.4
|
||||||
|
|
||||||
|
* bug: fix undefined variable on solaris (#493)
|
||||||
|
|
||||||
|
# 0.11.3
|
||||||
|
|
||||||
|
* formatter: configure quoting of empty values (#484)
|
||||||
|
* formatter: configure quoting character (default is `"`) (#484)
|
||||||
|
* bug: fix not importing io correctly in non-linux environments (#481)
|
||||||
|
|
||||||
|
# 0.11.2
|
||||||
|
|
||||||
|
* bug: fix windows terminal detection (#476)
|
||||||
|
|
||||||
|
# 0.11.1
|
||||||
|
|
||||||
|
* bug: fix tty detection with custom out (#471)
|
||||||
|
|
||||||
|
# 0.11.0
|
||||||
|
|
||||||
|
* performance: Use bufferpool to allocate (#370)
|
||||||
|
* terminal: terminal detection for app-engine (#343)
|
||||||
|
* feature: exit handler (#375)
|
||||||
|
|
||||||
|
# 0.10.0
|
||||||
|
|
||||||
|
* feature: Add a test hook (#180)
|
||||||
|
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||||
|
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||||
|
* performance: avoid re-allocations on `WithFields` (#335)
|
||||||
|
|
||||||
|
# 0.9.0
|
||||||
|
|
||||||
|
* logrus/text_formatter: don't emit empty msg
|
||||||
|
* logrus/hooks/airbrake: move out of main repository
|
||||||
|
* logrus/hooks/sentry: move out of main repository
|
||||||
|
* logrus/hooks/papertrail: move out of main repository
|
||||||
|
* logrus/hooks/bugsnag: move out of main repository
|
||||||
|
* logrus/core: run tests with `-race`
|
||||||
|
* logrus/core: detect TTY based on `stderr`
|
||||||
|
* logrus/core: support `WithError` on logger
|
||||||
|
* logrus/core: Solaris support
|
||||||
|
|
||||||
|
# 0.8.7
|
||||||
|
|
||||||
|
* logrus/core: fix possible race (#216)
|
||||||
|
* logrus/doc: small typo fixes and doc improvements
|
||||||
|
|
||||||
|
|
||||||
|
# 0.8.6
|
||||||
|
|
||||||
|
* hooks/raven: allow passing an initialized client
|
||||||
|
|
||||||
|
# 0.8.5
|
||||||
|
|
||||||
|
* logrus/core: revert #208
|
||||||
|
|
||||||
|
# 0.8.4
|
||||||
|
|
||||||
|
* formatter/text: fix data race (#218)
|
||||||
|
|
||||||
|
# 0.8.3
|
||||||
|
|
||||||
|
* logrus/core: fix entry log level (#208)
|
||||||
|
* logrus/core: improve performance of text formatter by 40%
|
||||||
|
* logrus/core: expose `LevelHooks` type
|
||||||
|
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||||
|
* formatter/text: print structs more verbosely
|
||||||
|
|
||||||
|
# 0.8.2
|
||||||
|
|
||||||
|
* logrus: fix more Fatal family functions
|
||||||
|
|
||||||
|
# 0.8.1
|
||||||
|
|
||||||
|
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||||
|
|
||||||
|
# 0.8.0
|
||||||
|
|
||||||
|
* logrus: defaults to stderr instead of stdout
|
||||||
|
* hooks/sentry: add special field for `*http.Request`
|
||||||
|
* formatter/text: ignore Windows for colors
|
||||||
|
|
||||||
|
# 0.7.3
|
||||||
|
|
||||||
|
* formatter/\*: allow configuration of timestamp layout
|
||||||
|
|
||||||
|
# 0.7.2
|
||||||
|
|
||||||
|
* formatter/text: Add configuration option for time format (#158)
|
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
Normal file
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Simon Eskildsen
|
||||||
|
|
||||||
|
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.
|
507
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
Normal file
507
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/sirupsen/logrus) [](https://godoc.org/github.com/sirupsen/logrus)
|
||||||
|
|
||||||
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
|
the standard library logger.
|
||||||
|
|
||||||
|
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
||||||
|
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
||||||
|
this caused issues in the community and we needed a standard. Some environments
|
||||||
|
experienced problems with the upper-case variant, so the lower-case was decided.
|
||||||
|
Everything using `logrus` will need to use the lower-case:
|
||||||
|
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
||||||
|
|
||||||
|
To fix Glide, see [these
|
||||||
|
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||||
|
For an in-depth explanation of the casing issue, see [this
|
||||||
|
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
||||||
|
|
||||||
|
**Are you interested in assisting in maintaining Logrus?** Currently I have a
|
||||||
|
lot of obligations, and I am unable to provide Logrus with the maintainership it
|
||||||
|
needs. If you'd like to help, please reach out to me at `simon at author's
|
||||||
|
username dot com`.
|
||||||
|
|
||||||
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
|
plain text):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||||
|
or Splunk:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||||
|
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||||
|
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||||
|
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||||
|
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||||
|
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||||
|
attached, the output is compatible with the
|
||||||
|
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||||
|
exit status 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Case-sensitivity
|
||||||
|
|
||||||
|
The organization's name was changed to lower-case--and this will not be changed
|
||||||
|
back. If you are getting import conflicts due to case sensitivity, please use
|
||||||
|
the lower-case import: `github.com/sirupsen/logrus`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||||
|
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
||||||
|
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||||
|
want:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Log as JSON instead of the default ASCII formatter.
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
|
||||||
|
// Output to stdout instead of the default stderr
|
||||||
|
// Can be any io.Writer, see below for File example
|
||||||
|
log.SetOutput(os.Stdout)
|
||||||
|
|
||||||
|
// Only log the warning severity or above.
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
|
||||||
|
// A common pattern is to re-use fields between logging statements by re-using
|
||||||
|
// the logrus.Entry returned from WithFields()
|
||||||
|
contextLogger := log.WithFields(log.Fields{
|
||||||
|
"common": "this is a common field",
|
||||||
|
"other": "I also should be logged always",
|
||||||
|
})
|
||||||
|
|
||||||
|
contextLogger.Info("I'll be logged with common and other field")
|
||||||
|
contextLogger.Info("Me too")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more advanced usage such as logging to multiple locations from the same
|
||||||
|
application, you can also create an instance of the `logrus` Logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new instance of the logger. You can have any number of instances.
|
||||||
|
var log = logrus.New()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// The API for setting attributes is a little different than the package level
|
||||||
|
// exported logger. See Godoc.
|
||||||
|
log.Out = os.Stdout
|
||||||
|
|
||||||
|
// You could set this to any `io.Writer` such as a file
|
||||||
|
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||||
|
// if err == nil {
|
||||||
|
// log.Out = file
|
||||||
|
// } else {
|
||||||
|
// log.Info("Failed to log to file, using default stderr")
|
||||||
|
// }
|
||||||
|
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
Logrus encourages careful, structured logging through logging fields instead of
|
||||||
|
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||||
|
to send event %s to topic %s with key %d")`, you should log the much more
|
||||||
|
discoverable:
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"event": event,
|
||||||
|
"topic": topic,
|
||||||
|
"key": key,
|
||||||
|
}).Fatal("Failed to send event")
|
||||||
|
```
|
||||||
|
|
||||||
|
We've found this API forces you to think about logging in a way that produces
|
||||||
|
much more useful logging messages. We've been in countless situations where just
|
||||||
|
a single added field to a log statement that was already there would've saved us
|
||||||
|
hours. The `WithFields` call is optional.
|
||||||
|
|
||||||
|
In general, with Logrus using any of the `printf`-family functions should be
|
||||||
|
seen as a hint you should add a field, however, you can still use the
|
||||||
|
`printf`-family functions with Logrus.
|
||||||
|
|
||||||
|
#### Default Fields
|
||||||
|
|
||||||
|
Often it's helpful to have fields _always_ attached to log statements in an
|
||||||
|
application or parts of one. For example, you may want to always log the
|
||||||
|
`request_id` and `user_ip` in the context of a request. Instead of writing
|
||||||
|
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
||||||
|
every line, you can create a `logrus.Entry` to pass around instead:
|
||||||
|
|
||||||
|
```go
|
||||||
|
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
||||||
|
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
||||||
|
requestLogger.Warn("something not great happened")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hooks
|
||||||
|
|
||||||
|
You can add hooks for logging levels. For example to send errors to an exception
|
||||||
|
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||||
|
multiple places simultaneously, e.g. syslog.
|
||||||
|
|
||||||
|
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||||
|
`init`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
||||||
|
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||||
|
"log/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||||
|
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||||
|
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||||
|
|
||||||
|
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to connect to local syslog daemon")
|
||||||
|
} else {
|
||||||
|
log.AddHook(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||||
|
|
||||||
|
| Hook | Description |
|
||||||
|
| ----- | ----------- |
|
||||||
|
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
||||||
|
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
||||||
|
| [Amazon Kinesis](https://github.com/evalphobia/logrus_kinesis) | Hook for logging to [Amazon Kinesis](https://aws.amazon.com/kinesis/) |
|
||||||
|
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||||
|
| [AzureTableHook](https://github.com/kpfaulkner/azuretablehook/) | Hook for logging to Azure Table Storage|
|
||||||
|
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||||
|
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||||
|
| [Discordrus](https://github.com/kz/discordrus) | Hook for logging to [Discord](https://discordapp.com/) |
|
||||||
|
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||||
|
| [Firehose](https://github.com/beaubrewer/logrus_firehose) | Hook for logging to [Amazon Firehose](https://aws.amazon.com/kinesis/firehose/)
|
||||||
|
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
|
||||||
|
| [Go-Slack](https://github.com/multiplay/go-slack) | Hook for logging to [Slack](https://slack.com) |
|
||||||
|
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
|
||||||
|
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
||||||
|
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
|
||||||
|
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
|
||||||
|
| [Influxus](http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB](http://influxdata.com/) |
|
||||||
|
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
||||||
|
| [KafkaLogrus](https://github.com/tracer0tong/kafkalogrus) | Hook for logging to Kafka |
|
||||||
|
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
||||||
|
| [Logentries](https://github.com/jcftang/logentriesrus) | Hook for logging to [Logentries](https://logentries.com/) |
|
||||||
|
| [Logentrus](https://github.com/puddingfactory/logentrus) | Hook for logging to [Logentries](https://logentries.com/) |
|
||||||
|
| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
|
||||||
|
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
||||||
|
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
|
||||||
|
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
|
||||||
|
| [Mattermost](https://github.com/shuLhan/mattermost-integration/tree/master/hooks/logrus) | Hook for logging to [Mattermost](https://mattermost.com/) |
|
||||||
|
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
|
||||||
|
| [NATS-Hook](https://github.com/rybit/nats_logrus_hook) | Hook for logging to [NATS](https://nats.io) |
|
||||||
|
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
||||||
|
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
|
||||||
|
| [PostgreSQL](https://github.com/gemnasium/logrus-postgresql-hook) | Send logs to [PostgreSQL](http://postgresql.org) |
|
||||||
|
| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
|
||||||
|
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
|
||||||
|
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
||||||
|
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
|
||||||
|
| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
|
||||||
|
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
|
||||||
|
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
|
||||||
|
| [Stackdriver](https://github.com/knq/sdhook) | Hook for logging to [Google Stackdriver](https://cloud.google.com/logging/) |
|
||||||
|
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
|
||||||
|
| [Syslog](https://github.com/sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
||||||
|
| [Syslog TLS](https://github.com/shinji62/logrus-syslog-ng) | Send errors to remote syslog server with TLS support. |
|
||||||
|
| [Telegram](https://github.com/rossmcdonald/telegram_hook) | Hook for logging errors to [Telegram](https://telegram.org/) |
|
||||||
|
| [TraceView](https://github.com/evalphobia/logrus_appneta) | Hook for logging to [AppNeta TraceView](https://www.appneta.com/products/traceview/) |
|
||||||
|
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
||||||
|
| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
|
||||||
|
| [SQS-Hook](https://github.com/tsarpaul/logrus_sqs) | Hook for logging to [Amazon Simple Queue Service (SQS)](https://aws.amazon.com/sqs/) |
|
||||||
|
|
||||||
|
#### Level logging
|
||||||
|
|
||||||
|
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||||
|
|
||||||
|
```go
|
||||||
|
log.Debug("Useful debugging information.")
|
||||||
|
log.Info("Something noteworthy happened!")
|
||||||
|
log.Warn("You should probably take a look at this.")
|
||||||
|
log.Error("Something failed but I'm not quitting.")
|
||||||
|
// Calls os.Exit(1) after logging
|
||||||
|
log.Fatal("Bye.")
|
||||||
|
// Calls panic() after logging
|
||||||
|
log.Panic("I'm bailing.")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the logging level on a `Logger`, then it will only log entries with
|
||||||
|
that severity or anything above it:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
```
|
||||||
|
|
||||||
|
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||||
|
environment if your application has that.
|
||||||
|
|
||||||
|
#### Entries
|
||||||
|
|
||||||
|
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||||
|
automatically added to all logging events:
|
||||||
|
|
||||||
|
1. `time`. The timestamp when the entry was created.
|
||||||
|
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||||
|
the `AddFields` call. E.g. `Failed to send event.`
|
||||||
|
3. `level`. The logging level. E.g. `info`.
|
||||||
|
|
||||||
|
#### Environments
|
||||||
|
|
||||||
|
Logrus has no notion of environment.
|
||||||
|
|
||||||
|
If you wish for hooks and formatters to only be used in specific environments,
|
||||||
|
you should handle that yourself. For example, if your application has a global
|
||||||
|
variable `Environment`, which is a string representation of the environment you
|
||||||
|
could do:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// do something here to set environment depending on an environment variable
|
||||||
|
// or command-line flag
|
||||||
|
if Environment == "production" {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
} else {
|
||||||
|
// The TextFormatter is default, you don't actually have to do this.
|
||||||
|
log.SetFormatter(&log.TextFormatter{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration is how `logrus` was intended to be used, but JSON in
|
||||||
|
production is mostly only useful if you do log aggregation with tools like
|
||||||
|
Splunk or Logstash.
|
||||||
|
|
||||||
|
#### Formatters
|
||||||
|
|
||||||
|
The built-in logging formatters are:
|
||||||
|
|
||||||
|
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||||
|
without colors.
|
||||||
|
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||||
|
field to `true`. To force no colored output even if there is a TTY set the
|
||||||
|
`DisableColors` field to `true`. For Windows, see
|
||||||
|
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||||
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
||||||
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
||||||
|
|
||||||
|
Third party logging formatters:
|
||||||
|
|
||||||
|
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can by parsed by Kubernetes and Google Container Engine.
|
||||||
|
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||||
|
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||||
|
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||||
|
|
||||||
|
You can define your formatter by implementing the `Formatter` interface,
|
||||||
|
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||||
|
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||||
|
default ones (see Entries section above):
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyJSONFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetFormatter(new(MyJSONFormatter))
|
||||||
|
|
||||||
|
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
// Note this doesn't include Time, Level and Message which are available on
|
||||||
|
// the Entry. Consult `godoc` on information about those fields or read the
|
||||||
|
// source of the official loggers.
|
||||||
|
serialized, err := json.Marshal(entry.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logger as an `io.Writer`
|
||||||
|
|
||||||
|
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||||
|
|
||||||
|
```go
|
||||||
|
w := logger.Writer()
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
// create a stdlib log.Logger that writes to
|
||||||
|
// logrus.Logger.
|
||||||
|
ErrorLog: log.New(w, "", 0),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line written to that writer will be printed the usual way, using formatters
|
||||||
|
and hooks. The level for those entries is `info`.
|
||||||
|
|
||||||
|
This means that we can override the standard library logger easily:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger := logrus.New()
|
||||||
|
logger.Formatter = &logrus.JSONFormatter{}
|
||||||
|
|
||||||
|
// Use logrus for standard log output
|
||||||
|
// Note that `log` here references stdlib's log
|
||||||
|
// Not logrus imported under the name `log`.
|
||||||
|
log.SetOutput(logger.Writer())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rotation
|
||||||
|
|
||||||
|
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||||
|
external program (like `logrotate(8)`) that can compress and delete old log
|
||||||
|
entries. It should not be a feature of the application-level logger.
|
||||||
|
|
||||||
|
#### Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||||
|
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
|
||||||
|
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||||
|
|
||||||
|
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||||
|
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t*testing.T){
|
||||||
|
logger, hook := test.NewNullLogger()
|
||||||
|
logger.Error("Helloerror")
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(hook.Entries))
|
||||||
|
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
||||||
|
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
||||||
|
|
||||||
|
hook.Reset()
|
||||||
|
assert.Nil(t, hook.LastEntry())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fatal handlers
|
||||||
|
|
||||||
|
Logrus can register one or more functions that will be called when any `fatal`
|
||||||
|
level message is logged. The registered handlers will be executed before
|
||||||
|
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
|
||||||
|
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
handler := func() {
|
||||||
|
// gracefully shutdown something...
|
||||||
|
}
|
||||||
|
logrus.RegisterExitHandler(handler)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Thread safety
|
||||||
|
|
||||||
|
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
|
||||||
|
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
||||||
|
|
||||||
|
Situation when locking is not needed includes:
|
||||||
|
|
||||||
|
* You have no hooks registered, or hooks calling is already thread-safe.
|
||||||
|
|
||||||
|
* Writing to logger.Out is already thread-safe, for example:
|
||||||
|
|
||||||
|
1) logger.Out is protected by locks.
|
||||||
|
|
||||||
|
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
|
||||||
|
|
||||||
|
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
64
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
64
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
// The following code was sourced and modified from the
|
||||||
|
// https://github.com/tebeka/atexit package governed by the following license:
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var handlers = []func(){}
|
||||||
|
|
||||||
|
func runHandler(handler func()) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHandlers() {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
runHandler(handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||||
|
func Exit(code int) {
|
||||||
|
runHandlers()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||||
|
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||||
|
// made.
|
||||||
|
//
|
||||||
|
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||||
|
// message but also needs to gracefully shutdown. An example usecase could be
|
||||||
|
// closing database connections, or sending a alert that the application is
|
||||||
|
// closing.
|
||||||
|
func RegisterExitHandler(handler func()) {
|
||||||
|
handlers = append(handlers, handler)
|
||||||
|
}
|
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
version: "{build}"
|
||||||
|
platform: x64
|
||||||
|
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
||||||
|
environment:
|
||||||
|
GOPATH: c:\gopath
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
install:
|
||||||
|
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||||
|
- go version
|
||||||
|
build_script:
|
||||||
|
- go get -t
|
||||||
|
- go test
|
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
Normal file
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||||
|
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"number": 1,
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||||
|
|
||||||
|
For a full guide visit https://github.com/sirupsen/logrus
|
||||||
|
*/
|
||||||
|
package logrus
|
279
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
Normal file
279
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufferPool *sync.Pool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defines the key when adding errors using WithError.
|
||||||
|
var ErrorKey = "error"
|
||||||
|
|
||||||
|
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||||
|
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||||
|
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||||
|
// passed around as much as you wish to avoid field duplication.
|
||||||
|
type Entry struct {
|
||||||
|
Logger *Logger
|
||||||
|
|
||||||
|
// Contains all the fields set by the user.
|
||||||
|
Data Fields
|
||||||
|
|
||||||
|
// Time at which the log entry was created
|
||||||
|
Time time.Time
|
||||||
|
|
||||||
|
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||||
|
Buffer *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntry(logger *Logger) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
Logger: logger,
|
||||||
|
// Default is three fields, give a little extra room
|
||||||
|
Data: make(Fields, 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the string representation from the reader and ultimately the
|
||||||
|
// formatter.
|
||||||
|
func (entry *Entry) String() (string, error) {
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str := string(serialized)
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||||
|
func (entry *Entry) WithError(err error) *Entry {
|
||||||
|
return entry.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single field to the Entry.
|
||||||
|
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||||
|
return entry.WithFields(Fields{key: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a map of fields to the Entry.
|
||||||
|
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
|
data := make(Fields, len(entry.Data)+len(fields))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range fields {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is not declared with a pointer value because otherwise
|
||||||
|
// race conditions will occur when using multiple goroutines
|
||||||
|
func (entry Entry) log(level Level, msg string) {
|
||||||
|
var buffer *bytes.Buffer
|
||||||
|
entry.Time = time.Now()
|
||||||
|
entry.Level = level
|
||||||
|
entry.Message = msg
|
||||||
|
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
err := entry.Logger.Hooks.Fire(level, &entry)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||||
|
buffer.Reset()
|
||||||
|
defer bufferPool.Put(buffer)
|
||||||
|
entry.Buffer = buffer
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||||
|
entry.Buffer = nil
|
||||||
|
if err != nil {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
} else {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
_, err = entry.Logger.Out.Write(serialized)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
|
}
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
// directly here.
|
||||||
|
if level <= PanicLevel {
|
||||||
|
panic(&entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Print(args ...interface{}) {
|
||||||
|
entry.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Info(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.log(InfoLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warn(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.log(WarnLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warning(args ...interface{}) {
|
||||||
|
entry.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Error(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.log(ErrorLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
panic(fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Printf family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.Debug(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.Info(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.Warn(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.Error(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.Fatal(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.Panic(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Println family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Debugln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= DebugLevel {
|
||||||
|
entry.Debug(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infoln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= InfoLevel {
|
||||||
|
entry.Info(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Println(args ...interface{}) {
|
||||||
|
entry.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= WarnLevel {
|
||||||
|
entry.Warn(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningln(args ...interface{}) {
|
||||||
|
entry.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= ErrorLevel {
|
||||||
|
entry.Error(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= FatalLevel {
|
||||||
|
entry.Fatal(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicln(args ...interface{}) {
|
||||||
|
if entry.Logger.level() >= PanicLevel {
|
||||||
|
entry.Panic(entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||||
|
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||||
|
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||||
|
// string allocation, we do the simplest thing.
|
||||||
|
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||||
|
msg := fmt.Sprintln(args...)
|
||||||
|
return msg[:len(msg)-1]
|
||||||
|
}
|
193
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
Normal file
193
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// std is the name of the standard logger in stdlib `log`
|
||||||
|
std = New()
|
||||||
|
)
|
||||||
|
|
||||||
|
func StandardLogger() *Logger {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the standard logger output.
|
||||||
|
func SetOutput(out io.Writer) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Out = out
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the standard logger formatter.
|
||||||
|
func SetFormatter(formatter Formatter) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Formatter = formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the standard logger level.
|
||||||
|
func SetLevel(level Level) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.SetLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the standard logger level.
|
||||||
|
func GetLevel() Level {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
return std.level()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHook adds a hook to the standard logger hooks.
|
||||||
|
func AddHook(hook Hook) {
|
||||||
|
std.mu.Lock()
|
||||||
|
defer std.mu.Unlock()
|
||||||
|
std.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||||
|
func WithError(err error) *Entry {
|
||||||
|
return std.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField creates an entry from the standard logger and adds a field to
|
||||||
|
// it. If you want multiple fields, use `WithFields`.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithField(key string, value interface{}) *Entry {
|
||||||
|
return std.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields creates an entry from the standard logger and adds multiple
|
||||||
|
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||||
|
// once for each field.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithFields(fields Fields) *Entry {
|
||||||
|
return std.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a message at level Debug on the standard logger.
|
||||||
|
func Debug(args ...interface{}) {
|
||||||
|
std.Debug(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print logs a message at level Info on the standard logger.
|
||||||
|
func Print(args ...interface{}) {
|
||||||
|
std.Print(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a message at level Info on the standard logger.
|
||||||
|
func Info(args ...interface{}) {
|
||||||
|
std.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a message at level Warn on the standard logger.
|
||||||
|
func Warn(args ...interface{}) {
|
||||||
|
std.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs a message at level Warn on the standard logger.
|
||||||
|
func Warning(args ...interface{}) {
|
||||||
|
std.Warning(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at level Error on the standard logger.
|
||||||
|
func Error(args ...interface{}) {
|
||||||
|
std.Error(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic logs a message at level Panic on the standard logger.
|
||||||
|
func Panic(args ...interface{}) {
|
||||||
|
std.Panic(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatal(args ...interface{}) {
|
||||||
|
std.Fatal(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a message at level Debug on the standard logger.
|
||||||
|
func Debugf(format string, args ...interface{}) {
|
||||||
|
std.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf logs a message at level Info on the standard logger.
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
std.Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a message at level Info on the standard logger.
|
||||||
|
func Infof(format string, args ...interface{}) {
|
||||||
|
std.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a message at level Warn on the standard logger.
|
||||||
|
func Warnf(format string, args ...interface{}) {
|
||||||
|
std.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs a message at level Warn on the standard logger.
|
||||||
|
func Warningf(format string, args ...interface{}) {
|
||||||
|
std.Warningf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a message at level Error on the standard logger.
|
||||||
|
func Errorf(format string, args ...interface{}) {
|
||||||
|
std.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicf logs a message at level Panic on the standard logger.
|
||||||
|
func Panicf(format string, args ...interface{}) {
|
||||||
|
std.Panicf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatalf(format string, args ...interface{}) {
|
||||||
|
std.Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a message at level Debug on the standard logger.
|
||||||
|
func Debugln(args ...interface{}) {
|
||||||
|
std.Debugln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs a message at level Info on the standard logger.
|
||||||
|
func Println(args ...interface{}) {
|
||||||
|
std.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a message at level Info on the standard logger.
|
||||||
|
func Infoln(args ...interface{}) {
|
||||||
|
std.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a message at level Warn on the standard logger.
|
||||||
|
func Warnln(args ...interface{}) {
|
||||||
|
std.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningln logs a message at level Warn on the standard logger.
|
||||||
|
func Warningln(args ...interface{}) {
|
||||||
|
std.Warningln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln logs a message at level Error on the standard logger.
|
||||||
|
func Errorln(args ...interface{}) {
|
||||||
|
std.Errorln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicln logs a message at level Panic on the standard logger.
|
||||||
|
func Panicln(args ...interface{}) {
|
||||||
|
std.Panicln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln logs a message at level Fatal on the standard logger.
|
||||||
|
func Fatalln(args ...interface{}) {
|
||||||
|
std.Fatalln(args...)
|
||||||
|
}
|
45
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
Normal file
45
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
const defaultTimestampFormat = time.RFC3339
|
||||||
|
|
||||||
|
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||||
|
// `Entry`. It exposes all the fields, including the default ones:
|
||||||
|
//
|
||||||
|
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||||
|
// * `entry.Data["time"]`. The timestamp.
|
||||||
|
// * `entry.Data["level"]. The level the entry was logged at.
|
||||||
|
//
|
||||||
|
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||||
|
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||||
|
// logged to `logger.Out`.
|
||||||
|
type Formatter interface {
|
||||||
|
Format(*Entry) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to not silently overwrite `time`, `msg` and `level` fields when
|
||||||
|
// dumping it. If this code wasn't there doing:
|
||||||
|
//
|
||||||
|
// logrus.WithField("level", 1).Info("hello")
|
||||||
|
//
|
||||||
|
// Would just silently drop the user provided level. Instead with this code
|
||||||
|
// it'll logged as:
|
||||||
|
//
|
||||||
|
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||||
|
//
|
||||||
|
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||||
|
// avoid code duplication between the two default formatters.
|
||||||
|
func prefixFieldClashes(data Fields) {
|
||||||
|
if t, ok := data["time"]; ok {
|
||||||
|
data["fields.time"] = t
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := data["msg"]; ok {
|
||||||
|
data["fields.msg"] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
if l, ok := data["level"]; ok {
|
||||||
|
data["fields.level"] = l
|
||||||
|
}
|
||||||
|
}
|
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
Normal file
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
// A hook to be fired when logging on the logging levels returned from
|
||||||
|
// `Levels()` on your implementation of the interface. Note that this is not
|
||||||
|
// fired in a goroutine or a channel with workers, you should handle such
|
||||||
|
// functionality yourself if your call is non-blocking and you don't wish for
|
||||||
|
// the logging calls for levels returned from `Levels()` to block.
|
||||||
|
type Hook interface {
|
||||||
|
Levels() []Level
|
||||||
|
Fire(*Entry) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal type for storing the hooks on a logger instance.
|
||||||
|
type LevelHooks map[Level][]Hook
|
||||||
|
|
||||||
|
// Add a hook to an instance of logger. This is called with
|
||||||
|
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||||
|
func (hooks LevelHooks) Add(hook Hook) {
|
||||||
|
for _, level := range hook.Levels() {
|
||||||
|
hooks[level] = append(hooks[level], hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||||
|
// appropriate hooks for a log entry.
|
||||||
|
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||||
|
for _, hook := range hooks[level] {
|
||||||
|
if err := hook.Fire(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
79
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
79
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fieldKey string
|
||||||
|
|
||||||
|
// FieldMap allows customization of the key names for default fields.
|
||||||
|
type FieldMap map[fieldKey]string
|
||||||
|
|
||||||
|
// Default key names for the default fields
|
||||||
|
const (
|
||||||
|
FieldKeyMsg = "msg"
|
||||||
|
FieldKeyLevel = "level"
|
||||||
|
FieldKeyTime = "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f FieldMap) resolve(key fieldKey) string {
|
||||||
|
if k, ok := f[key]; ok {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONFormatter formats logs into parsable json
|
||||||
|
type JSONFormatter struct {
|
||||||
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for default fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &JSONFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyMsg: "@message",
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
FieldMap FieldMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
data := make(Fields, len(entry.Data)+3)
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case error:
|
||||||
|
// Otherwise errors are ignored by `encoding/json`
|
||||||
|
// https://github.com/sirupsen/logrus/issues/137
|
||||||
|
data[k] = v.Error()
|
||||||
|
default:
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefixFieldClashes(data)
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||||
|
}
|
||||||
|
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||||
|
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||||
|
|
||||||
|
serialized, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
323
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
Normal file
323
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||||
|
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||||
|
// something more adventorous, such as logging to Kafka.
|
||||||
|
Out io.Writer
|
||||||
|
// Hooks for the logger instance. These allow firing events based on logging
|
||||||
|
// levels and log entries. For example, to send errors to an error tracking
|
||||||
|
// service, log to StatsD or dump the core on fatal errors.
|
||||||
|
Hooks LevelHooks
|
||||||
|
// All log entries pass through the formatter before logged to Out. The
|
||||||
|
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||||
|
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||||
|
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||||
|
// own that implements the `Formatter` interface, see the `README` or included
|
||||||
|
// formatters for examples.
|
||||||
|
Formatter Formatter
|
||||||
|
// The logging level the logger should log at. This is typically (and defaults
|
||||||
|
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||||
|
// logged.
|
||||||
|
Level Level
|
||||||
|
// Used to sync writing to the log. Locking is enabled by Default
|
||||||
|
mu MutexWrap
|
||||||
|
// Reusable empty entry
|
||||||
|
entryPool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
type MutexWrap struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
disabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Lock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Lock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Unlock() {
|
||||||
|
if !mw.disabled {
|
||||||
|
mw.lock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mw *MutexWrap) Disable() {
|
||||||
|
mw.disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||||
|
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||||
|
// instantiate your own:
|
||||||
|
//
|
||||||
|
// var log = &Logger{
|
||||||
|
// Out: os.Stderr,
|
||||||
|
// Formatter: new(JSONFormatter),
|
||||||
|
// Hooks: make(LevelHooks),
|
||||||
|
// Level: logrus.DebugLevel,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It's recommended to make this a global instance called `log`.
|
||||||
|
func New() *Logger {
|
||||||
|
return &Logger{
|
||||||
|
Out: os.Stderr,
|
||||||
|
Formatter: new(TextFormatter),
|
||||||
|
Hooks: make(LevelHooks),
|
||||||
|
Level: InfoLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) newEntry() *Entry {
|
||||||
|
entry, ok := logger.entryPool.Get().(*Entry)
|
||||||
|
if ok {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return NewEntry(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||||
|
logger.entryPool.Put(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a field to the log entry, note that it doesn't log until you call
|
||||||
|
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||||
|
// If you want multiple fields, use `WithFields`.
|
||||||
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||||
|
// each `Field`.
|
||||||
|
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an error as single field to the log entry. All it does is call
|
||||||
|
// `WithError` for the given `error`.
|
||||||
|
func (logger *Logger) WithError(err error) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debugf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Printf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Errorf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panicf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debug(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Info(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warn(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Error(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatal(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panic(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
|
if logger.level() >= DebugLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Debugln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
|
if logger.level() >= InfoLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Infoln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Println(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
|
if logger.level() >= WarnLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Warnln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
|
if logger.level() >= ErrorLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Errorln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
|
if logger.level() >= FatalLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Fatalln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
|
if logger.level() >= PanicLevel {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Panicln(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//When file is opened with appending mode, it's safe to
|
||||||
|
//write concurrently to a file (within 4k message on Linux).
|
||||||
|
//In these cases user can choose to disable the lock.
|
||||||
|
func (logger *Logger) SetNoLock() {
|
||||||
|
logger.mu.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) level() Level {
|
||||||
|
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetLevel(level Level) {
|
||||||
|
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) AddHook(hook Hook) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Hooks.Add(hook)
|
||||||
|
}
|
143
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
Normal file
143
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fields type, used to pass to `WithFields`.
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
type Level uint32
|
||||||
|
|
||||||
|
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||||
|
func (level Level) String() string {
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
return "debug"
|
||||||
|
case InfoLevel:
|
||||||
|
return "info"
|
||||||
|
case WarnLevel:
|
||||||
|
return "warning"
|
||||||
|
case ErrorLevel:
|
||||||
|
return "error"
|
||||||
|
case FatalLevel:
|
||||||
|
return "fatal"
|
||||||
|
case PanicLevel:
|
||||||
|
return "panic"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) (Level, error) {
|
||||||
|
switch strings.ToLower(lvl) {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel, nil
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel, nil
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel, nil
|
||||||
|
case "warn", "warning":
|
||||||
|
return WarnLevel, nil
|
||||||
|
case "info":
|
||||||
|
return InfoLevel, nil
|
||||||
|
case "debug":
|
||||||
|
return DebugLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var l Level
|
||||||
|
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A constant exposing all logging levels
|
||||||
|
var AllLevels = []Level{
|
||||||
|
PanicLevel,
|
||||||
|
FatalLevel,
|
||||||
|
ErrorLevel,
|
||||||
|
WarnLevel,
|
||||||
|
InfoLevel,
|
||||||
|
DebugLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the different logging levels. You can set the logging level to log
|
||||||
|
// on your instance of logger, obtained with `logrus.New()`.
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||||
|
var (
|
||||||
|
_ StdLogger = &log.Logger{}
|
||||||
|
_ StdLogger = &Entry{}
|
||||||
|
_ StdLogger = &Logger{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdLogger is what your logrus-enabled library should take, that way
|
||||||
|
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||||
|
// interface, this is the closest we get, unfortunately.
|
||||||
|
type StdLogger interface {
|
||||||
|
Print(...interface{})
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
|
||||||
|
Fatal(...interface{})
|
||||||
|
Fatalf(string, ...interface{})
|
||||||
|
Fatalln(...interface{})
|
||||||
|
|
||||||
|
Panic(...interface{})
|
||||||
|
Panicf(string, ...interface{})
|
||||||
|
Panicln(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// The FieldLogger interface generalizes the Entry and Logger types
|
||||||
|
type FieldLogger interface {
|
||||||
|
WithField(key string, value interface{}) *Entry
|
||||||
|
WithFields(fields Fields) *Entry
|
||||||
|
WithError(err error) *Entry
|
||||||
|
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
Printf(format string, args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
Warningf(format string, args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Panicf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Info(args ...interface{})
|
||||||
|
Print(args ...interface{})
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warning(args ...interface{})
|
||||||
|
Error(args ...interface{})
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Panic(args ...interface{})
|
||||||
|
|
||||||
|
Debugln(args ...interface{})
|
||||||
|
Infoln(args ...interface{})
|
||||||
|
Println(args ...interface{})
|
||||||
|
Warnln(args ...interface{})
|
||||||
|
Warningln(args ...interface{})
|
||||||
|
Errorln(args ...interface{})
|
||||||
|
Fatalln(args ...interface{})
|
||||||
|
Panicln(args ...interface{})
|
||||||
|
}
|
10
vendor/github.com/sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
10
vendor/github.com/sirupsen/logrus/terminal_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build darwin freebsd openbsd netbsd dragonfly
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TIOCGETA
|
||||||
|
|
||||||
|
type Termios unix.Termios
|
14
vendor/github.com/sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
14
vendor/github.com/sirupsen/logrus/terminal_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Based on ssh/terminal:
|
||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TCGETS
|
||||||
|
|
||||||
|
type Termios unix.Termios
|
191
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
191
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nocolor = 0
|
||||||
|
red = 31
|
||||||
|
green = 32
|
||||||
|
yellow = 33
|
||||||
|
blue = 36
|
||||||
|
gray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseTimestamp time.Time
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseTimestamp = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextFormatter formats logs into text
|
||||||
|
type TextFormatter struct {
|
||||||
|
// Set to true to bypass checking for a TTY before outputting colors.
|
||||||
|
ForceColors bool
|
||||||
|
|
||||||
|
// Force disabling colors.
|
||||||
|
DisableColors bool
|
||||||
|
|
||||||
|
// Disable timestamp logging. useful when output is redirected to logging
|
||||||
|
// system that already adds timestamps.
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// Enable logging the full timestamp when a TTY is attached instead of just
|
||||||
|
// the time passed since beginning of execution.
|
||||||
|
FullTimestamp bool
|
||||||
|
|
||||||
|
// TimestampFormat to use for display when a full timestamp is printed
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
// that log extremely frequently and don't use the JSON formatter this may not
|
||||||
|
// be desired.
|
||||||
|
DisableSorting bool
|
||||||
|
|
||||||
|
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||||
|
QuoteEmptyFields bool
|
||||||
|
|
||||||
|
// Whether the logger's out is to a terminal
|
||||||
|
isTerminal bool
|
||||||
|
|
||||||
|
sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
|
if entry.Logger != nil {
|
||||||
|
f.isTerminal = f.checkIfTerminal(entry.Logger.Out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) checkIfTerminal(w io.Writer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *os.File:
|
||||||
|
return terminal.IsTerminal(int(v.Fd()))
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
var b *bytes.Buffer
|
||||||
|
keys := make([]string, 0, len(entry.Data))
|
||||||
|
for k := range entry.Data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableSorting {
|
||||||
|
sort.Strings(keys)
|
||||||
|
}
|
||||||
|
if entry.Buffer != nil {
|
||||||
|
b = entry.Buffer
|
||||||
|
} else {
|
||||||
|
b = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixFieldClashes(entry.Data)
|
||||||
|
|
||||||
|
f.Do(func() { f.init(entry) })
|
||||||
|
|
||||||
|
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
if isColored {
|
||||||
|
f.printColored(b, entry, keys, timestampFormat)
|
||||||
|
} else {
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
|
||||||
|
}
|
||||||
|
f.appendKeyValue(b, "level", entry.Level.String())
|
||||||
|
if entry.Message != "" {
|
||||||
|
f.appendKeyValue(b, "msg", entry.Message)
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
f.appendKeyValue(b, key, entry.Data[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('\n')
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
||||||
|
var levelColor int
|
||||||
|
switch entry.Level {
|
||||||
|
case DebugLevel:
|
||||||
|
levelColor = gray
|
||||||
|
case WarnLevel:
|
||||||
|
levelColor = yellow
|
||||||
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
|
levelColor = red
|
||||||
|
default:
|
||||||
|
levelColor = blue
|
||||||
|
}
|
||||||
|
|
||||||
|
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||||
|
|
||||||
|
if f.DisableTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||||
|
} else if !f.FullTimestamp {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
v := entry.Data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||||
|
f.appendValue(b, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||||
|
if f.QuoteEmptyFields && len(text) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, ch := range text {
|
||||||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||||
|
if b.Len() > 0 {
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
||||||
|
b.WriteString(key)
|
||||||
|
b.WriteByte('=')
|
||||||
|
f.appendValue(b, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||||
|
stringVal, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
stringVal = fmt.Sprint(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.needsQuoting(stringVal) {
|
||||||
|
b.WriteString(stringVal)
|
||||||
|
} else {
|
||||||
|
b.WriteString(fmt.Sprintf("%q", stringVal))
|
||||||
|
}
|
||||||
|
}
|
62
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
Normal file
62
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
return logger.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
return NewEntry(logger).WriterLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
|
return entry.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
printFunc = entry.Debug
|
||||||
|
case InfoLevel:
|
||||||
|
printFunc = entry.Info
|
||||||
|
case WarnLevel:
|
||||||
|
printFunc = entry.Warn
|
||||||
|
case ErrorLevel:
|
||||||
|
printFunc = entry.Error
|
||||||
|
case FatalLevel:
|
||||||
|
printFunc = entry.Fatal
|
||||||
|
case PanicLevel:
|
||||||
|
printFunc = entry.Panic
|
||||||
|
default:
|
||||||
|
printFunc = entry.Print
|
||||||
|
}
|
||||||
|
|
||||||
|
go entry.writerScanner(reader, printFunc)
|
||||||
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
for scanner.Scan() {
|
||||||
|
printFunc(scanner.Text())
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
|
}
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
|
writer.Close()
|
||||||
|
}
|
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 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.
|
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
951
vendor/golang.org/x/crypto/ssh/terminal/terminal.go
generated
vendored
Normal file
951
vendor/golang.org/x/crypto/ssh/terminal/terminal.go
generated
vendored
Normal file
|
@ -0,0 +1,951 @@
|
||||||
|
// 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 terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EscapeCodes contains escape sequences that can be written to the terminal in
|
||||||
|
// order to achieve different styles of text.
|
||||||
|
type EscapeCodes struct {
|
||||||
|
// Foreground colors
|
||||||
|
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
|
||||||
|
|
||||||
|
// Reset all attributes
|
||||||
|
Reset []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var vt100EscapeCodes = EscapeCodes{
|
||||||
|
Black: []byte{keyEscape, '[', '3', '0', 'm'},
|
||||||
|
Red: []byte{keyEscape, '[', '3', '1', 'm'},
|
||||||
|
Green: []byte{keyEscape, '[', '3', '2', 'm'},
|
||||||
|
Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
|
||||||
|
Blue: []byte{keyEscape, '[', '3', '4', 'm'},
|
||||||
|
Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
|
||||||
|
Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
|
||||||
|
White: []byte{keyEscape, '[', '3', '7', 'm'},
|
||||||
|
|
||||||
|
Reset: []byte{keyEscape, '[', '0', 'm'},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminal contains the state for running a VT100 terminal that is capable of
|
||||||
|
// reading lines of input.
|
||||||
|
type Terminal struct {
|
||||||
|
// AutoCompleteCallback, if non-null, is called for each keypress with
|
||||||
|
// the full input line and the current position of the cursor (in
|
||||||
|
// bytes, as an index into |line|). If it returns ok=false, the key
|
||||||
|
// press is processed normally. Otherwise it returns a replacement line
|
||||||
|
// and the new cursor position.
|
||||||
|
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
|
||||||
|
|
||||||
|
// Escape contains a pointer to the escape codes for this terminal.
|
||||||
|
// It's always a valid pointer, although the escape codes themselves
|
||||||
|
// may be empty if the terminal doesn't support them.
|
||||||
|
Escape *EscapeCodes
|
||||||
|
|
||||||
|
// lock protects the terminal and the state in this object from
|
||||||
|
// concurrent processing of a key press and a Write() call.
|
||||||
|
lock sync.Mutex
|
||||||
|
|
||||||
|
c io.ReadWriter
|
||||||
|
prompt []rune
|
||||||
|
|
||||||
|
// line is the current line being entered.
|
||||||
|
line []rune
|
||||||
|
// pos is the logical position of the cursor in line
|
||||||
|
pos int
|
||||||
|
// echo is true if local echo is enabled
|
||||||
|
echo bool
|
||||||
|
// pasteActive is true iff there is a bracketed paste operation in
|
||||||
|
// progress.
|
||||||
|
pasteActive bool
|
||||||
|
|
||||||
|
// cursorX contains the current X value of the cursor where the left
|
||||||
|
// edge is 0. cursorY contains the row number where the first row of
|
||||||
|
// the current line is 0.
|
||||||
|
cursorX, cursorY int
|
||||||
|
// maxLine is the greatest value of cursorY so far.
|
||||||
|
maxLine int
|
||||||
|
|
||||||
|
termWidth, termHeight int
|
||||||
|
|
||||||
|
// outBuf contains the terminal data to be sent.
|
||||||
|
outBuf []byte
|
||||||
|
// remainder contains the remainder of any partial key sequences after
|
||||||
|
// a read. It aliases into inBuf.
|
||||||
|
remainder []byte
|
||||||
|
inBuf [256]byte
|
||||||
|
|
||||||
|
// history contains previously entered commands so that they can be
|
||||||
|
// accessed with the up and down keys.
|
||||||
|
history stRingBuffer
|
||||||
|
// historyIndex stores the currently accessed history entry, where zero
|
||||||
|
// means the immediately previous entry.
|
||||||
|
historyIndex int
|
||||||
|
// When navigating up and down the history it's possible to return to
|
||||||
|
// the incomplete, initial line. That value is stored in
|
||||||
|
// historyPending.
|
||||||
|
historyPending string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
|
||||||
|
// a local terminal, that terminal must first have been put into raw mode.
|
||||||
|
// prompt is a string that is written at the start of each input line (i.e.
|
||||||
|
// "> ").
|
||||||
|
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
|
||||||
|
return &Terminal{
|
||||||
|
Escape: &vt100EscapeCodes,
|
||||||
|
c: c,
|
||||||
|
prompt: []rune(prompt),
|
||||||
|
termWidth: 80,
|
||||||
|
termHeight: 24,
|
||||||
|
echo: true,
|
||||||
|
historyIndex: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyCtrlD = 4
|
||||||
|
keyCtrlU = 21
|
||||||
|
keyEnter = '\r'
|
||||||
|
keyEscape = 27
|
||||||
|
keyBackspace = 127
|
||||||
|
keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
|
||||||
|
keyUp
|
||||||
|
keyDown
|
||||||
|
keyLeft
|
||||||
|
keyRight
|
||||||
|
keyAltLeft
|
||||||
|
keyAltRight
|
||||||
|
keyHome
|
||||||
|
keyEnd
|
||||||
|
keyDeleteWord
|
||||||
|
keyDeleteLine
|
||||||
|
keyClearScreen
|
||||||
|
keyPasteStart
|
||||||
|
keyPasteEnd
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
crlf = []byte{'\r', '\n'}
|
||||||
|
pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
|
||||||
|
pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
|
||||||
|
)
|
||||||
|
|
||||||
|
// bytesToKey tries to parse a key sequence from b. If successful, it returns
|
||||||
|
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
|
||||||
|
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return utf8.RuneError, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pasteActive {
|
||||||
|
switch b[0] {
|
||||||
|
case 1: // ^A
|
||||||
|
return keyHome, b[1:]
|
||||||
|
case 5: // ^E
|
||||||
|
return keyEnd, b[1:]
|
||||||
|
case 8: // ^H
|
||||||
|
return keyBackspace, b[1:]
|
||||||
|
case 11: // ^K
|
||||||
|
return keyDeleteLine, b[1:]
|
||||||
|
case 12: // ^L
|
||||||
|
return keyClearScreen, b[1:]
|
||||||
|
case 23: // ^W
|
||||||
|
return keyDeleteWord, b[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[0] != keyEscape {
|
||||||
|
if !utf8.FullRune(b) {
|
||||||
|
return utf8.RuneError, b
|
||||||
|
}
|
||||||
|
r, l := utf8.DecodeRune(b)
|
||||||
|
return r, b[l:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
|
||||||
|
switch b[2] {
|
||||||
|
case 'A':
|
||||||
|
return keyUp, b[3:]
|
||||||
|
case 'B':
|
||||||
|
return keyDown, b[3:]
|
||||||
|
case 'C':
|
||||||
|
return keyRight, b[3:]
|
||||||
|
case 'D':
|
||||||
|
return keyLeft, b[3:]
|
||||||
|
case 'H':
|
||||||
|
return keyHome, b[3:]
|
||||||
|
case 'F':
|
||||||
|
return keyEnd, b[3:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
|
||||||
|
switch b[5] {
|
||||||
|
case 'C':
|
||||||
|
return keyAltRight, b[6:]
|
||||||
|
case 'D':
|
||||||
|
return keyAltLeft, b[6:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
|
||||||
|
return keyPasteStart, b[6:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
|
||||||
|
return keyPasteEnd, b[6:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here then we have a key that we don't recognise, or a
|
||||||
|
// partial sequence. It's not clear how one should find the end of a
|
||||||
|
// sequence without knowing them all, but it seems that [a-zA-Z~] only
|
||||||
|
// appears at the end of a sequence.
|
||||||
|
for i, c := range b[0:] {
|
||||||
|
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
|
||||||
|
return keyUnknown, b[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return utf8.RuneError, b
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue appends data to the end of t.outBuf
|
||||||
|
func (t *Terminal) queue(data []rune) {
|
||||||
|
t.outBuf = append(t.outBuf, []byte(string(data))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
|
||||||
|
var space = []rune{' '}
|
||||||
|
|
||||||
|
func isPrintable(key rune) bool {
|
||||||
|
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
|
||||||
|
return key >= 32 && !isInSurrogateArea
|
||||||
|
}
|
||||||
|
|
||||||
|
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
|
||||||
|
// given, logical position in the text.
|
||||||
|
func (t *Terminal) moveCursorToPos(pos int) {
|
||||||
|
if !t.echo {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x := visualLength(t.prompt) + pos
|
||||||
|
y := x / t.termWidth
|
||||||
|
x = x % t.termWidth
|
||||||
|
|
||||||
|
up := 0
|
||||||
|
if y < t.cursorY {
|
||||||
|
up = t.cursorY - y
|
||||||
|
}
|
||||||
|
|
||||||
|
down := 0
|
||||||
|
if y > t.cursorY {
|
||||||
|
down = y - t.cursorY
|
||||||
|
}
|
||||||
|
|
||||||
|
left := 0
|
||||||
|
if x < t.cursorX {
|
||||||
|
left = t.cursorX - x
|
||||||
|
}
|
||||||
|
|
||||||
|
right := 0
|
||||||
|
if x > t.cursorX {
|
||||||
|
right = x - t.cursorX
|
||||||
|
}
|
||||||
|
|
||||||
|
t.cursorX = x
|
||||||
|
t.cursorY = y
|
||||||
|
t.move(up, down, left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) move(up, down, left, right int) {
|
||||||
|
movement := make([]rune, 3*(up+down+left+right))
|
||||||
|
m := movement
|
||||||
|
for i := 0; i < up; i++ {
|
||||||
|
m[0] = keyEscape
|
||||||
|
m[1] = '['
|
||||||
|
m[2] = 'A'
|
||||||
|
m = m[3:]
|
||||||
|
}
|
||||||
|
for i := 0; i < down; i++ {
|
||||||
|
m[0] = keyEscape
|
||||||
|
m[1] = '['
|
||||||
|
m[2] = 'B'
|
||||||
|
m = m[3:]
|
||||||
|
}
|
||||||
|
for i := 0; i < left; i++ {
|
||||||
|
m[0] = keyEscape
|
||||||
|
m[1] = '['
|
||||||
|
m[2] = 'D'
|
||||||
|
m = m[3:]
|
||||||
|
}
|
||||||
|
for i := 0; i < right; i++ {
|
||||||
|
m[0] = keyEscape
|
||||||
|
m[1] = '['
|
||||||
|
m[2] = 'C'
|
||||||
|
m = m[3:]
|
||||||
|
}
|
||||||
|
|
||||||
|
t.queue(movement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) clearLineToRight() {
|
||||||
|
op := []rune{keyEscape, '[', 'K'}
|
||||||
|
t.queue(op)
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxLineLength = 4096
|
||||||
|
|
||||||
|
func (t *Terminal) setLine(newLine []rune, newPos int) {
|
||||||
|
if t.echo {
|
||||||
|
t.moveCursorToPos(0)
|
||||||
|
t.writeLine(newLine)
|
||||||
|
for i := len(newLine); i < len(t.line); i++ {
|
||||||
|
t.writeLine(space)
|
||||||
|
}
|
||||||
|
t.moveCursorToPos(newPos)
|
||||||
|
}
|
||||||
|
t.line = newLine
|
||||||
|
t.pos = newPos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) advanceCursor(places int) {
|
||||||
|
t.cursorX += places
|
||||||
|
t.cursorY += t.cursorX / t.termWidth
|
||||||
|
if t.cursorY > t.maxLine {
|
||||||
|
t.maxLine = t.cursorY
|
||||||
|
}
|
||||||
|
t.cursorX = t.cursorX % t.termWidth
|
||||||
|
|
||||||
|
if places > 0 && t.cursorX == 0 {
|
||||||
|
// Normally terminals will advance the current position
|
||||||
|
// when writing a character. But that doesn't happen
|
||||||
|
// for the last character in a line. However, when
|
||||||
|
// writing a character (except a new line) that causes
|
||||||
|
// a line wrap, the position will be advanced two
|
||||||
|
// places.
|
||||||
|
//
|
||||||
|
// So, if we are stopping at the end of a line, we
|
||||||
|
// need to write a newline so that our cursor can be
|
||||||
|
// advanced to the next line.
|
||||||
|
t.outBuf = append(t.outBuf, '\r', '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) eraseNPreviousChars(n int) {
|
||||||
|
if n == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.pos < n {
|
||||||
|
n = t.pos
|
||||||
|
}
|
||||||
|
t.pos -= n
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
|
||||||
|
copy(t.line[t.pos:], t.line[n+t.pos:])
|
||||||
|
t.line = t.line[:len(t.line)-n]
|
||||||
|
if t.echo {
|
||||||
|
t.writeLine(t.line[t.pos:])
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
t.queue(space)
|
||||||
|
}
|
||||||
|
t.advanceCursor(n)
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// countToLeftWord returns then number of characters from the cursor to the
|
||||||
|
// start of the previous word.
|
||||||
|
func (t *Terminal) countToLeftWord() int {
|
||||||
|
if t.pos == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := t.pos - 1
|
||||||
|
for pos > 0 {
|
||||||
|
if t.line[pos] != ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos--
|
||||||
|
}
|
||||||
|
for pos > 0 {
|
||||||
|
if t.line[pos] == ' ' {
|
||||||
|
pos++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos--
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.pos - pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// countToRightWord returns then number of characters from the cursor to the
|
||||||
|
// start of the next word.
|
||||||
|
func (t *Terminal) countToRightWord() int {
|
||||||
|
pos := t.pos
|
||||||
|
for pos < len(t.line) {
|
||||||
|
if t.line[pos] == ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
for pos < len(t.line) {
|
||||||
|
if t.line[pos] != ' ' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos - t.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// visualLength returns the number of visible glyphs in s.
|
||||||
|
func visualLength(runes []rune) int {
|
||||||
|
inEscapeSeq := false
|
||||||
|
length := 0
|
||||||
|
|
||||||
|
for _, r := range runes {
|
||||||
|
switch {
|
||||||
|
case inEscapeSeq:
|
||||||
|
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
|
||||||
|
inEscapeSeq = false
|
||||||
|
}
|
||||||
|
case r == '\x1b':
|
||||||
|
inEscapeSeq = true
|
||||||
|
default:
|
||||||
|
length++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleKey processes the given key and, optionally, returns a line of text
|
||||||
|
// that the user has entered.
|
||||||
|
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
||||||
|
if t.pasteActive && key != keyEnter {
|
||||||
|
t.addKeyToLine(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case keyBackspace:
|
||||||
|
if t.pos == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.eraseNPreviousChars(1)
|
||||||
|
case keyAltLeft:
|
||||||
|
// move left by a word.
|
||||||
|
t.pos -= t.countToLeftWord()
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyAltRight:
|
||||||
|
// move right by a word.
|
||||||
|
t.pos += t.countToRightWord()
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyLeft:
|
||||||
|
if t.pos == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.pos--
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyRight:
|
||||||
|
if t.pos == len(t.line) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.pos++
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyHome:
|
||||||
|
if t.pos == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.pos = 0
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyEnd:
|
||||||
|
if t.pos == len(t.line) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.pos = len(t.line)
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyUp:
|
||||||
|
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if t.historyIndex == -1 {
|
||||||
|
t.historyPending = string(t.line)
|
||||||
|
}
|
||||||
|
t.historyIndex++
|
||||||
|
runes := []rune(entry)
|
||||||
|
t.setLine(runes, len(runes))
|
||||||
|
case keyDown:
|
||||||
|
switch t.historyIndex {
|
||||||
|
case -1:
|
||||||
|
return
|
||||||
|
case 0:
|
||||||
|
runes := []rune(t.historyPending)
|
||||||
|
t.setLine(runes, len(runes))
|
||||||
|
t.historyIndex--
|
||||||
|
default:
|
||||||
|
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
|
||||||
|
if ok {
|
||||||
|
t.historyIndex--
|
||||||
|
runes := []rune(entry)
|
||||||
|
t.setLine(runes, len(runes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case keyEnter:
|
||||||
|
t.moveCursorToPos(len(t.line))
|
||||||
|
t.queue([]rune("\r\n"))
|
||||||
|
line = string(t.line)
|
||||||
|
ok = true
|
||||||
|
t.line = t.line[:0]
|
||||||
|
t.pos = 0
|
||||||
|
t.cursorX = 0
|
||||||
|
t.cursorY = 0
|
||||||
|
t.maxLine = 0
|
||||||
|
case keyDeleteWord:
|
||||||
|
// Delete zero or more spaces and then one or more characters.
|
||||||
|
t.eraseNPreviousChars(t.countToLeftWord())
|
||||||
|
case keyDeleteLine:
|
||||||
|
// Delete everything from the current cursor position to the
|
||||||
|
// end of line.
|
||||||
|
for i := t.pos; i < len(t.line); i++ {
|
||||||
|
t.queue(space)
|
||||||
|
t.advanceCursor(1)
|
||||||
|
}
|
||||||
|
t.line = t.line[:t.pos]
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
case keyCtrlD:
|
||||||
|
// Erase the character under the current position.
|
||||||
|
// The EOF case when the line is empty is handled in
|
||||||
|
// readLine().
|
||||||
|
if t.pos < len(t.line) {
|
||||||
|
t.pos++
|
||||||
|
t.eraseNPreviousChars(1)
|
||||||
|
}
|
||||||
|
case keyCtrlU:
|
||||||
|
t.eraseNPreviousChars(t.pos)
|
||||||
|
case keyClearScreen:
|
||||||
|
// Erases the screen and moves the cursor to the home position.
|
||||||
|
t.queue([]rune("\x1b[2J\x1b[H"))
|
||||||
|
t.queue(t.prompt)
|
||||||
|
t.cursorX, t.cursorY = 0, 0
|
||||||
|
t.advanceCursor(visualLength(t.prompt))
|
||||||
|
t.setLine(t.line, t.pos)
|
||||||
|
default:
|
||||||
|
if t.AutoCompleteCallback != nil {
|
||||||
|
prefix := string(t.line[:t.pos])
|
||||||
|
suffix := string(t.line[t.pos:])
|
||||||
|
|
||||||
|
t.lock.Unlock()
|
||||||
|
newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
|
||||||
|
t.lock.Lock()
|
||||||
|
|
||||||
|
if completeOk {
|
||||||
|
t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isPrintable(key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(t.line) == maxLineLength {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.addKeyToLine(key)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// addKeyToLine inserts the given key at the current position in the current
|
||||||
|
// line.
|
||||||
|
func (t *Terminal) addKeyToLine(key rune) {
|
||||||
|
if len(t.line) == cap(t.line) {
|
||||||
|
newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
|
||||||
|
copy(newLine, t.line)
|
||||||
|
t.line = newLine
|
||||||
|
}
|
||||||
|
t.line = t.line[:len(t.line)+1]
|
||||||
|
copy(t.line[t.pos+1:], t.line[t.pos:])
|
||||||
|
t.line[t.pos] = key
|
||||||
|
if t.echo {
|
||||||
|
t.writeLine(t.line[t.pos:])
|
||||||
|
}
|
||||||
|
t.pos++
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) writeLine(line []rune) {
|
||||||
|
for len(line) != 0 {
|
||||||
|
remainingOnLine := t.termWidth - t.cursorX
|
||||||
|
todo := len(line)
|
||||||
|
if todo > remainingOnLine {
|
||||||
|
todo = remainingOnLine
|
||||||
|
}
|
||||||
|
t.queue(line[:todo])
|
||||||
|
t.advanceCursor(visualLength(line[:todo]))
|
||||||
|
line = line[todo:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
|
||||||
|
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
|
||||||
|
for len(buf) > 0 {
|
||||||
|
i := bytes.IndexByte(buf, '\n')
|
||||||
|
todo := len(buf)
|
||||||
|
if i >= 0 {
|
||||||
|
todo = i
|
||||||
|
}
|
||||||
|
|
||||||
|
var nn int
|
||||||
|
nn, err = w.Write(buf[:todo])
|
||||||
|
n += nn
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
buf = buf[todo:]
|
||||||
|
|
||||||
|
if i >= 0 {
|
||||||
|
if _, err = w.Write(crlf); err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n += 1
|
||||||
|
buf = buf[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) Write(buf []byte) (n int, err error) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
if t.cursorX == 0 && t.cursorY == 0 {
|
||||||
|
// This is the easy case: there's nothing on the screen that we
|
||||||
|
// have to move out of the way.
|
||||||
|
return writeWithCRLF(t.c, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a prompt and possibly user input on the screen. We
|
||||||
|
// have to clear it first.
|
||||||
|
t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
|
||||||
|
t.cursorX = 0
|
||||||
|
t.clearLineToRight()
|
||||||
|
|
||||||
|
for t.cursorY > 0 {
|
||||||
|
t.move(1 /* up */, 0, 0, 0)
|
||||||
|
t.cursorY--
|
||||||
|
t.clearLineToRight()
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = t.c.Write(t.outBuf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.outBuf = t.outBuf[:0]
|
||||||
|
|
||||||
|
if n, err = writeWithCRLF(t.c, buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.writeLine(t.prompt)
|
||||||
|
if t.echo {
|
||||||
|
t.writeLine(t.line)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
|
||||||
|
if _, err = t.c.Write(t.outBuf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.outBuf = t.outBuf[:0]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword temporarily changes the prompt and reads a password, without
|
||||||
|
// echo, from the terminal.
|
||||||
|
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
oldPrompt := t.prompt
|
||||||
|
t.prompt = []rune(prompt)
|
||||||
|
t.echo = false
|
||||||
|
|
||||||
|
line, err = t.readLine()
|
||||||
|
|
||||||
|
t.prompt = oldPrompt
|
||||||
|
t.echo = true
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLine returns a line of input from the terminal.
|
||||||
|
func (t *Terminal) ReadLine() (line string, err error) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
return t.readLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) readLine() (line string, err error) {
|
||||||
|
// t.lock must be held at this point
|
||||||
|
|
||||||
|
if t.cursorX == 0 && t.cursorY == 0 {
|
||||||
|
t.writeLine(t.prompt)
|
||||||
|
t.c.Write(t.outBuf)
|
||||||
|
t.outBuf = t.outBuf[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
lineIsPasted := t.pasteActive
|
||||||
|
|
||||||
|
for {
|
||||||
|
rest := t.remainder
|
||||||
|
lineOk := false
|
||||||
|
for !lineOk {
|
||||||
|
var key rune
|
||||||
|
key, rest = bytesToKey(rest, t.pasteActive)
|
||||||
|
if key == utf8.RuneError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !t.pasteActive {
|
||||||
|
if key == keyCtrlD {
|
||||||
|
if len(t.line) == 0 {
|
||||||
|
return "", io.EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key == keyPasteStart {
|
||||||
|
t.pasteActive = true
|
||||||
|
if len(t.line) == 0 {
|
||||||
|
lineIsPasted = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if key == keyPasteEnd {
|
||||||
|
t.pasteActive = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !t.pasteActive {
|
||||||
|
lineIsPasted = false
|
||||||
|
}
|
||||||
|
line, lineOk = t.handleKey(key)
|
||||||
|
}
|
||||||
|
if len(rest) > 0 {
|
||||||
|
n := copy(t.inBuf[:], rest)
|
||||||
|
t.remainder = t.inBuf[:n]
|
||||||
|
} else {
|
||||||
|
t.remainder = nil
|
||||||
|
}
|
||||||
|
t.c.Write(t.outBuf)
|
||||||
|
t.outBuf = t.outBuf[:0]
|
||||||
|
if lineOk {
|
||||||
|
if t.echo {
|
||||||
|
t.historyIndex = -1
|
||||||
|
t.history.Add(line)
|
||||||
|
}
|
||||||
|
if lineIsPasted {
|
||||||
|
err = ErrPasteIndicator
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.remainder is a slice at the beginning of t.inBuf
|
||||||
|
// containing a partial key sequence
|
||||||
|
readBuf := t.inBuf[len(t.remainder):]
|
||||||
|
var n int
|
||||||
|
|
||||||
|
t.lock.Unlock()
|
||||||
|
n, err = t.c.Read(readBuf)
|
||||||
|
t.lock.Lock()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.remainder = t.inBuf[:n+len(t.remainder)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrompt sets the prompt to be used when reading subsequent lines.
|
||||||
|
func (t *Terminal) SetPrompt(prompt string) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
t.prompt = []rune(prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
|
||||||
|
// Move cursor to column zero at the start of the line.
|
||||||
|
t.move(t.cursorY, 0, t.cursorX, 0)
|
||||||
|
t.cursorX, t.cursorY = 0, 0
|
||||||
|
t.clearLineToRight()
|
||||||
|
for t.cursorY < numPrevLines {
|
||||||
|
// Move down a line
|
||||||
|
t.move(0, 1, 0, 0)
|
||||||
|
t.cursorY++
|
||||||
|
t.clearLineToRight()
|
||||||
|
}
|
||||||
|
// Move back to beginning.
|
||||||
|
t.move(t.cursorY, 0, 0, 0)
|
||||||
|
t.cursorX, t.cursorY = 0, 0
|
||||||
|
|
||||||
|
t.queue(t.prompt)
|
||||||
|
t.advanceCursor(visualLength(t.prompt))
|
||||||
|
t.writeLine(t.line)
|
||||||
|
t.moveCursorToPos(t.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) SetSize(width, height int) error {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
if width == 0 {
|
||||||
|
width = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
oldWidth := t.termWidth
|
||||||
|
t.termWidth, t.termHeight = width, height
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case width == oldWidth:
|
||||||
|
// If the width didn't change then nothing else needs to be
|
||||||
|
// done.
|
||||||
|
return nil
|
||||||
|
case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
|
||||||
|
// If there is nothing on current line and no prompt printed,
|
||||||
|
// just do nothing
|
||||||
|
return nil
|
||||||
|
case width < oldWidth:
|
||||||
|
// Some terminals (e.g. xterm) will truncate lines that were
|
||||||
|
// too long when shinking. Others, (e.g. gnome-terminal) will
|
||||||
|
// attempt to wrap them. For the former, repainting t.maxLine
|
||||||
|
// works great, but that behaviour goes badly wrong in the case
|
||||||
|
// of the latter because they have doubled every full line.
|
||||||
|
|
||||||
|
// We assume that we are working on a terminal that wraps lines
|
||||||
|
// and adjust the cursor position based on every previous line
|
||||||
|
// wrapping and turning into two. This causes the prompt on
|
||||||
|
// xterms to move upwards, which isn't great, but it avoids a
|
||||||
|
// huge mess with gnome-terminal.
|
||||||
|
if t.cursorX >= t.termWidth {
|
||||||
|
t.cursorX = t.termWidth - 1
|
||||||
|
}
|
||||||
|
t.cursorY *= 2
|
||||||
|
t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
|
||||||
|
case width > oldWidth:
|
||||||
|
// If the terminal expands then our position calculations will
|
||||||
|
// be wrong in the future because we think the cursor is
|
||||||
|
// |t.pos| chars into the string, but there will be a gap at
|
||||||
|
// the end of any wrapped line.
|
||||||
|
//
|
||||||
|
// But the position will actually be correct until we move, so
|
||||||
|
// we can move back to the beginning and repaint everything.
|
||||||
|
t.clearAndRepaintLinePlusNPrevious(t.maxLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := t.c.Write(t.outBuf)
|
||||||
|
t.outBuf = t.outBuf[:0]
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type pasteIndicatorError struct{}
|
||||||
|
|
||||||
|
func (pasteIndicatorError) Error() string {
|
||||||
|
return "terminal: ErrPasteIndicator not correctly handled"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrPasteIndicator may be returned from ReadLine as the error, in addition
|
||||||
|
// to valid line data. It indicates that bracketed paste mode is enabled and
|
||||||
|
// that the returned line consists only of pasted data. Programs may wish to
|
||||||
|
// interpret pasted data more literally than typed data.
|
||||||
|
var ErrPasteIndicator = pasteIndicatorError{}
|
||||||
|
|
||||||
|
// SetBracketedPasteMode requests that the terminal bracket paste operations
|
||||||
|
// with markers. Not all terminals support this but, if it is supported, then
|
||||||
|
// enabling this mode will stop any autocomplete callback from running due to
|
||||||
|
// pastes. Additionally, any lines that are completely pasted will be returned
|
||||||
|
// from ReadLine with the error set to ErrPasteIndicator.
|
||||||
|
func (t *Terminal) SetBracketedPasteMode(on bool) {
|
||||||
|
if on {
|
||||||
|
io.WriteString(t.c, "\x1b[?2004h")
|
||||||
|
} else {
|
||||||
|
io.WriteString(t.c, "\x1b[?2004l")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stRingBuffer is a ring buffer of strings.
|
||||||
|
type stRingBuffer struct {
|
||||||
|
// entries contains max elements.
|
||||||
|
entries []string
|
||||||
|
max int
|
||||||
|
// head contains the index of the element most recently added to the ring.
|
||||||
|
head int
|
||||||
|
// size contains the number of elements in the ring.
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stRingBuffer) Add(a string) {
|
||||||
|
if s.entries == nil {
|
||||||
|
const defaultNumEntries = 100
|
||||||
|
s.entries = make([]string, defaultNumEntries)
|
||||||
|
s.max = defaultNumEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
s.head = (s.head + 1) % s.max
|
||||||
|
s.entries[s.head] = a
|
||||||
|
if s.size < s.max {
|
||||||
|
s.size++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NthPreviousEntry returns the value passed to the nth previous call to Add.
|
||||||
|
// If n is zero then the immediately prior value is returned, if one, then the
|
||||||
|
// next most recent, and so on. If such an element doesn't exist then ok is
|
||||||
|
// false.
|
||||||
|
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
|
||||||
|
if n >= s.size {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
index := s.head - n
|
||||||
|
if index < 0 {
|
||||||
|
index += s.max
|
||||||
|
}
|
||||||
|
return s.entries[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
||||||
|
// The slice returned does not include the \n.
|
||||||
|
// readPasswordLine also ignores any \r it finds.
|
||||||
|
func readPasswordLine(reader io.Reader) ([]byte, error) {
|
||||||
|
var buf [1]byte
|
||||||
|
var ret []byte
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := reader.Read(buf[:])
|
||||||
|
if n > 0 {
|
||||||
|
switch buf[0] {
|
||||||
|
case '\n':
|
||||||
|
return ret, nil
|
||||||
|
case '\r':
|
||||||
|
// remove \r from passwords on Windows
|
||||||
|
default:
|
||||||
|
ret = append(ret, buf[0])
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF && len(ret) > 0 {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
vendor/golang.org/x/crypto/ssh/terminal/util.go
generated
vendored
Normal file
123
vendor/golang.org/x/crypto/ssh/terminal/util.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
|
||||||
|
|
||||||
|
// Package terminal provides support functions for dealing with terminals, as
|
||||||
|
// commonly found on UNIX systems.
|
||||||
|
//
|
||||||
|
// Putting a terminal into raw mode is the most common requirement:
|
||||||
|
//
|
||||||
|
// oldState, err := terminal.MakeRaw(0)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer terminal.Restore(0, oldState)
|
||||||
|
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State contains the state of a terminal.
|
||||||
|
type State struct {
|
||||||
|
termios syscall.Termios
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd int) bool {
|
||||||
|
var termios syscall.Termios
|
||||||
|
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||||
|
return err == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||||
|
// the termios(3) manpage.
|
||||||
|
newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||||
|
newState.Oflag &^= syscall.OPOST
|
||||||
|
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||||
|
newState.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||||
|
newState.Cflag |= syscall.CS8
|
||||||
|
newState.Cc[unix.VMIN] = 1
|
||||||
|
newState.Cc[unix.VTIME] = 0
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize returns the dimensions of the given terminal.
|
||||||
|
func GetSize(fd int) (width, height int, err error) {
|
||||||
|
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 -1, -1, err
|
||||||
|
}
|
||||||
|
return int(dimensions[1]), int(dimensions[0]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// passwordReader is an io.Reader that reads from a specific file descriptor.
|
||||||
|
type passwordReader int
|
||||||
|
|
||||||
|
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||||
|
return syscall.Read(int(r), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
var oldState syscall.Termios
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState
|
||||||
|
newState.Lflag &^= syscall.ECHO
|
||||||
|
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
||||||
|
newState.Iflag |= syscall.ICRNL
|
||||||
|
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return readPasswordLine(passwordReader(fd))
|
||||||
|
}
|
12
vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
generated
vendored
Normal file
12
vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TIOCGETA
|
||||||
|
const ioctlWriteTermios = unix.TIOCSETA
|
10
vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
generated
vendored
Normal file
10
vendor/golang.org/x/crypto/ssh/terminal/util_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2013 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 terminal
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TCGETS
|
||||||
|
const ioctlWriteTermios = unix.TCSETS
|
58
vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
generated
vendored
Normal file
58
vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2016 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 terminal provides support functions for dealing with terminals, as
|
||||||
|
// commonly found on UNIX systems.
|
||||||
|
//
|
||||||
|
// Putting a terminal into raw mode is the most common requirement:
|
||||||
|
//
|
||||||
|
// oldState, err := terminal.MakeRaw(0)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer terminal.Restore(0, oldState)
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct{}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize returns the dimensions of the given terminal.
|
||||||
|
func GetSize(fd int) (width, height int, err error) {
|
||||||
|
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
128
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
Normal file
128
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State contains the state of a terminal.
|
||||||
|
type State struct {
|
||||||
|
state *unix.Termios
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd int) bool {
|
||||||
|
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
|
||||||
|
val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldState := *val
|
||||||
|
|
||||||
|
newState := oldState
|
||||||
|
newState.Lflag &^= syscall.ECHO
|
||||||
|
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
||||||
|
newState.Iflag |= syscall.ICRNL
|
||||||
|
err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
|
||||||
|
|
||||||
|
var buf [16]byte
|
||||||
|
var ret []byte
|
||||||
|
for {
|
||||||
|
n, err := syscall.Read(fd, buf[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
if len(ret) == 0 {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if buf[n-1] == '\n' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
ret = append(ret, buf[:n]...)
|
||||||
|
if n < len(buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldTermios := *oldTermiosPtr
|
||||||
|
|
||||||
|
newTermios := oldTermios
|
||||||
|
newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||||
|
newTermios.Oflag &^= syscall.OPOST
|
||||||
|
newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||||
|
newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||||
|
newTermios.Cflag |= syscall.CS8
|
||||||
|
newTermios.Cc[unix.VMIN] = 1
|
||||||
|
newTermios.Cc[unix.VTIME] = 0
|
||||||
|
|
||||||
|
if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &State{
|
||||||
|
state: oldTermiosPtr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, oldState *State) error {
|
||||||
|
return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &State{
|
||||||
|
state: oldTermiosPtr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize returns the dimensions of the given terminal.
|
||||||
|
func GetSize(fd int) (width, height int, err error) {
|
||||||
|
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return int(ws.Col), int(ws.Row), nil
|
||||||
|
}
|
102
vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
generated
vendored
Normal file
102
vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package terminal provides support functions for dealing with terminals, as
|
||||||
|
// commonly found on UNIX systems.
|
||||||
|
//
|
||||||
|
// Putting a terminal into raw mode is the most common requirement:
|
||||||
|
//
|
||||||
|
// oldState, err := terminal.MakeRaw(0)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer terminal.Restore(0, oldState)
|
||||||
|
package terminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
mode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd int) bool {
|
||||||
|
var st uint32
|
||||||
|
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
var st uint32
|
||||||
|
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||||
|
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &State{st}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
var st uint32
|
||||||
|
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &State{st}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSize returns the dimensions of the given terminal.
|
||||||
|
func GetSize(fd int) (width, height int, err error) {
|
||||||
|
var info windows.ConsoleScreenBufferInfo
|
||||||
|
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return int(info.Size.X), int(info.Size.Y), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
|
||||||
|
type passwordReader int
|
||||||
|
|
||||||
|
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||||
|
return windows.Read(windows.Handle(r), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||||
|
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||||
|
// returned does not include the \n.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
var st uint32
|
||||||
|
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
old := st
|
||||||
|
|
||||||
|
st &^= (windows.ENABLE_ECHO_INPUT)
|
||||||
|
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||||
|
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
windows.SetConsoleMode(windows.Handle(fd), old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return readPasswordLine(passwordReader(fd))
|
||||||
|
}
|
13
vendor/golang.org/x/sys/windows/asm_windows_386.s
generated
vendored
Normal file
13
vendor/golang.org/x/sys/windows/asm_windows_386.s
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for 386, Windows are implemented in runtime/syscall_windows.goc
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·getprocaddress(SB), 7, $0-8
|
||||||
|
JMP syscall·getprocaddress(SB)
|
||||||
|
|
||||||
|
TEXT ·loadlibrary(SB), 7, $0-4
|
||||||
|
JMP syscall·loadlibrary(SB)
|
13
vendor/golang.org/x/sys/windows/asm_windows_amd64.s
generated
vendored
Normal file
13
vendor/golang.org/x/sys/windows/asm_windows_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for amd64, Windows are implemented in runtime/syscall_windows.goc
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·getprocaddress(SB), 7, $0-32
|
||||||
|
JMP syscall·getprocaddress(SB)
|
||||||
|
|
||||||
|
TEXT ·loadlibrary(SB), 7, $0-8
|
||||||
|
JMP syscall·loadlibrary(SB)
|
377
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
Normal file
377
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
// 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 windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DLLError describes reasons for DLL load failures.
|
||||||
|
type DLLError struct {
|
||||||
|
Err error
|
||||||
|
ObjName string
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DLLError) Error() string { return e.Msg }
|
||||||
|
|
||||||
|
// Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file.
|
||||||
|
func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
|
||||||
|
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
|
||||||
|
|
||||||
|
// A DLL implements access to a single DLL.
|
||||||
|
type DLL struct {
|
||||||
|
Name string
|
||||||
|
Handle Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadDLL loads DLL file into memory.
|
||||||
|
//
|
||||||
|
// Warning: using LoadDLL without an absolute path name is subject to
|
||||||
|
// DLL preloading attacks. To safely load a system DLL, use LazyDLL
|
||||||
|
// with System set to true, or use LoadLibraryEx directly.
|
||||||
|
func LoadDLL(name string) (dll *DLL, err error) {
|
||||||
|
namep, err := UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, e := loadlibrary(namep)
|
||||||
|
if e != 0 {
|
||||||
|
return nil, &DLLError{
|
||||||
|
Err: e,
|
||||||
|
ObjName: name,
|
||||||
|
Msg: "Failed to load " + name + ": " + e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d := &DLL{
|
||||||
|
Name: name,
|
||||||
|
Handle: Handle(h),
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustLoadDLL is like LoadDLL but panics if load operation failes.
|
||||||
|
func MustLoadDLL(name string) *DLL {
|
||||||
|
d, e := LoadDLL(name)
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindProc searches DLL d for procedure named name and returns *Proc
|
||||||
|
// if found. It returns an error if search fails.
|
||||||
|
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
|
||||||
|
namep, err := BytePtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a, e := getprocaddress(uintptr(d.Handle), namep)
|
||||||
|
if e != 0 {
|
||||||
|
return nil, &DLLError{
|
||||||
|
Err: e,
|
||||||
|
ObjName: name,
|
||||||
|
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p := &Proc{
|
||||||
|
Dll: d,
|
||||||
|
Name: name,
|
||||||
|
addr: a,
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFindProc is like FindProc but panics if search fails.
|
||||||
|
func (d *DLL) MustFindProc(name string) *Proc {
|
||||||
|
p, e := d.FindProc(name)
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release unloads DLL d from memory.
|
||||||
|
func (d *DLL) Release() (err error) {
|
||||||
|
return FreeLibrary(d.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Proc implements access to a procedure inside a DLL.
|
||||||
|
type Proc struct {
|
||||||
|
Dll *DLL
|
||||||
|
Name string
|
||||||
|
addr uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the address of the procedure represented by p.
|
||||||
|
// The return value can be passed to Syscall to run the procedure.
|
||||||
|
func (p *Proc) Addr() uintptr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:uintptrescapes
|
||||||
|
|
||||||
|
// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
|
||||||
|
// are supplied.
|
||||||
|
//
|
||||||
|
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||||
|
// Callers must inspect the primary return value to decide whether an error occurred
|
||||||
|
// (according to the semantics of the specific function being called) before consulting
|
||||||
|
// the error. The error will be guaranteed to contain windows.Errno.
|
||||||
|
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||||
|
switch len(a) {
|
||||||
|
case 0:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
|
||||||
|
case 1:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
|
||||||
|
case 2:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
|
||||||
|
case 3:
|
||||||
|
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
|
||||||
|
case 4:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
|
||||||
|
case 5:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
|
||||||
|
case 6:
|
||||||
|
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
|
||||||
|
case 7:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
|
||||||
|
case 8:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
|
||||||
|
case 9:
|
||||||
|
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
|
||||||
|
case 10:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
|
||||||
|
case 11:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
|
||||||
|
case 12:
|
||||||
|
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
|
||||||
|
case 13:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
|
||||||
|
case 14:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
|
||||||
|
case 15:
|
||||||
|
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
|
||||||
|
default:
|
||||||
|
panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LazyDLL implements access to a single DLL.
|
||||||
|
// It will delay the load of the DLL until the first
|
||||||
|
// call to its Handle method or to one of its
|
||||||
|
// LazyProc's Addr method.
|
||||||
|
type LazyDLL struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// System determines whether the DLL must be loaded from the
|
||||||
|
// Windows System directory, bypassing the normal DLL search
|
||||||
|
// path.
|
||||||
|
System bool
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
dll *DLL // non nil once DLL is loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads DLL file d.Name into memory. It returns an error if fails.
|
||||||
|
// Load will not try to load DLL, if it is already loaded into memory.
|
||||||
|
func (d *LazyDLL) Load() error {
|
||||||
|
// Non-racy version of:
|
||||||
|
// if d.dll != nil {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.dll != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// kernel32.dll is special, since it's where LoadLibraryEx comes from.
|
||||||
|
// The kernel already special-cases its name, so it's always
|
||||||
|
// loaded from system32.
|
||||||
|
var dll *DLL
|
||||||
|
var err error
|
||||||
|
if d.Name == "kernel32.dll" {
|
||||||
|
dll, err = LoadDLL(d.Name)
|
||||||
|
} else {
|
||||||
|
dll, err = loadLibraryEx(d.Name, d.System)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-racy version of:
|
||||||
|
// d.dll = dll
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustLoad is like Load but panics if search fails.
|
||||||
|
func (d *LazyDLL) mustLoad() {
|
||||||
|
e := d.Load()
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle returns d's module handle.
|
||||||
|
func (d *LazyDLL) Handle() uintptr {
|
||||||
|
d.mustLoad()
|
||||||
|
return uintptr(d.dll.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
|
||||||
|
func (d *LazyDLL) NewProc(name string) *LazyProc {
|
||||||
|
return &LazyProc{l: d, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLazyDLL creates new LazyDLL associated with DLL file.
|
||||||
|
func NewLazyDLL(name string) *LazyDLL {
|
||||||
|
return &LazyDLL{Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLazySystemDLL is like NewLazyDLL, but will only
|
||||||
|
// search Windows System directory for the DLL if name is
|
||||||
|
// a base name (like "advapi32.dll").
|
||||||
|
func NewLazySystemDLL(name string) *LazyDLL {
|
||||||
|
return &LazyDLL{Name: name, System: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LazyProc implements access to a procedure inside a LazyDLL.
|
||||||
|
// It delays the lookup until the Addr method is called.
|
||||||
|
type LazyProc struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
l *LazyDLL
|
||||||
|
proc *Proc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find searches DLL for procedure named p.Name. It returns
|
||||||
|
// an error if search fails. Find will not search procedure,
|
||||||
|
// if it is already found and loaded into memory.
|
||||||
|
func (p *LazyProc) Find() error {
|
||||||
|
// Non-racy version of:
|
||||||
|
// if p.proc == nil {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.proc == nil {
|
||||||
|
e := p.l.Load()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
proc, e := p.l.dll.FindProc(p.Name)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// Non-racy version of:
|
||||||
|
// p.proc = proc
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustFind is like Find but panics if search fails.
|
||||||
|
func (p *LazyProc) mustFind() {
|
||||||
|
e := p.Find()
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the address of the procedure represented by p.
|
||||||
|
// The return value can be passed to Syscall to run the procedure.
|
||||||
|
func (p *LazyProc) Addr() uintptr {
|
||||||
|
p.mustFind()
|
||||||
|
return p.proc.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:uintptrescapes
|
||||||
|
|
||||||
|
// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
|
||||||
|
// are supplied.
|
||||||
|
//
|
||||||
|
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||||
|
// Callers must inspect the primary return value to decide whether an error occurred
|
||||||
|
// (according to the semantics of the specific function being called) before consulting
|
||||||
|
// the error. The error will be guaranteed to contain windows.Errno.
|
||||||
|
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||||
|
p.mustFind()
|
||||||
|
return p.proc.Call(a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var canDoSearchSystem32Once struct {
|
||||||
|
sync.Once
|
||||||
|
v bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCanDoSearchSystem32() {
|
||||||
|
// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
|
||||||
|
// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
|
||||||
|
// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
|
||||||
|
// systems that have KB2533623 installed. To determine whether the
|
||||||
|
// flags are available, use GetProcAddress to get the address of the
|
||||||
|
// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
|
||||||
|
// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
|
||||||
|
// flags can be used with LoadLibraryEx."
|
||||||
|
canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canDoSearchSystem32() bool {
|
||||||
|
canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
|
||||||
|
return canDoSearchSystem32Once.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBaseName(name string) bool {
|
||||||
|
for _, c := range name {
|
||||||
|
if c == ':' || c == '/' || c == '\\' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadLibraryEx wraps the Windows LoadLibraryEx function.
|
||||||
|
//
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
|
||||||
|
//
|
||||||
|
// If name is not an absolute path, LoadLibraryEx searches for the DLL
|
||||||
|
// in a variety of automatic locations unless constrained by flags.
|
||||||
|
// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
|
||||||
|
func loadLibraryEx(name string, system bool) (*DLL, error) {
|
||||||
|
loadDLL := name
|
||||||
|
var flags uintptr
|
||||||
|
if system {
|
||||||
|
if canDoSearchSystem32() {
|
||||||
|
const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||||
|
flags = LOAD_LIBRARY_SEARCH_SYSTEM32
|
||||||
|
} else if isBaseName(name) {
|
||||||
|
// WindowsXP or unpatched Windows machine
|
||||||
|
// trying to load "foo.dll" out of the system
|
||||||
|
// folder, but LoadLibraryEx doesn't support
|
||||||
|
// that yet on their system, so emulate it.
|
||||||
|
windir, _ := Getenv("WINDIR") // old var; apparently works on XP
|
||||||
|
if windir == "" {
|
||||||
|
return nil, errString("%WINDIR% not defined")
|
||||||
|
}
|
||||||
|
loadDLL = windir + "\\System32\\" + name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := LoadLibraryEx(loadDLL, 0, flags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DLL{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type errString string
|
||||||
|
|
||||||
|
func (s errString) Error() string { return string(s) }
|
15
vendor/golang.org/x/sys/windows/env_unset.go
generated
vendored
Normal file
15
vendor/golang.org/x/sys/windows/env_unset.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
// +build go1.4
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func Unsetenv(key string) error {
|
||||||
|
// This was added in Go 1.4.
|
||||||
|
return syscall.Unsetenv(key)
|
||||||
|
}
|
25
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
Normal file
25
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2010 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.
|
||||||
|
|
||||||
|
// Windows environment variables.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func Getenv(key string) (value string, found bool) {
|
||||||
|
return syscall.Getenv(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Setenv(key, value string) error {
|
||||||
|
return syscall.Setenv(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clearenv() {
|
||||||
|
syscall.Clearenv()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Environ() []string {
|
||||||
|
return syscall.Environ()
|
||||||
|
}
|
20
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
Normal file
20
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
EVENTLOG_SUCCESS = 0
|
||||||
|
EVENTLOG_ERROR_TYPE = 1
|
||||||
|
EVENTLOG_WARNING_TYPE = 2
|
||||||
|
EVENTLOG_INFORMATION_TYPE = 4
|
||||||
|
EVENTLOG_AUDIT_SUCCESS = 8
|
||||||
|
EVENTLOG_AUDIT_FAILURE = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) [failretval==0] = advapi32.RegisterEventSourceW
|
||||||
|
//sys DeregisterEventSource(handle Handle) (err error) = advapi32.DeregisterEventSource
|
||||||
|
//sys ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) = advapi32.ReportEventW
|
97
vendor/golang.org/x/sys/windows/exec_windows.go
generated
vendored
Normal file
97
vendor/golang.org/x/sys/windows/exec_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Fork, exec, wait, etc.
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
// EscapeArg rewrites command line argument s as prescribed
|
||||||
|
// in http://msdn.microsoft.com/en-us/library/ms880421.
|
||||||
|
// This function returns "" (2 double quotes) if s is empty.
|
||||||
|
// Alternatively, these transformations are done:
|
||||||
|
// - every back slash (\) is doubled, but only if immediately
|
||||||
|
// followed by double quote (");
|
||||||
|
// - every double quote (") is escaped by back slash (\);
|
||||||
|
// - finally, s is wrapped with double quotes (arg -> "arg"),
|
||||||
|
// but only if there is space or tab inside s.
|
||||||
|
func EscapeArg(s string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return "\"\""
|
||||||
|
}
|
||||||
|
n := len(s)
|
||||||
|
hasSpace := false
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
case '"', '\\':
|
||||||
|
n++
|
||||||
|
case ' ', '\t':
|
||||||
|
hasSpace = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasSpace {
|
||||||
|
n += 2
|
||||||
|
}
|
||||||
|
if n == len(s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := make([]byte, n)
|
||||||
|
j := 0
|
||||||
|
if hasSpace {
|
||||||
|
qs[j] = '"'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
slashes := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
switch s[i] {
|
||||||
|
default:
|
||||||
|
slashes = 0
|
||||||
|
qs[j] = s[i]
|
||||||
|
case '\\':
|
||||||
|
slashes++
|
||||||
|
qs[j] = s[i]
|
||||||
|
case '"':
|
||||||
|
for ; slashes > 0; slashes-- {
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
qs[j] = s[i]
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if hasSpace {
|
||||||
|
for ; slashes > 0; slashes-- {
|
||||||
|
qs[j] = '\\'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
qs[j] = '"'
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return string(qs[:j])
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseOnExec(fd Handle) {
|
||||||
|
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullPath retrieves the full path of the specified file.
|
||||||
|
func FullPath(name string) (path string, err error) {
|
||||||
|
p, err := UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
n := uint32(100)
|
||||||
|
for {
|
||||||
|
buf := make([]uint16, n)
|
||||||
|
n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(buf)) {
|
||||||
|
return UTF16ToString(buf[:n]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
vendor/golang.org/x/sys/windows/memory_windows.go
generated
vendored
Normal file
26
vendor/golang.org/x/sys/windows/memory_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2017 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 windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
MEM_COMMIT = 0x00001000
|
||||||
|
MEM_RESERVE = 0x00002000
|
||||||
|
MEM_DECOMMIT = 0x00004000
|
||||||
|
MEM_RELEASE = 0x00008000
|
||||||
|
MEM_RESET = 0x00080000
|
||||||
|
MEM_TOP_DOWN = 0x00100000
|
||||||
|
MEM_WRITE_WATCH = 0x00200000
|
||||||
|
MEM_PHYSICAL = 0x00400000
|
||||||
|
MEM_RESET_UNDO = 0x01000000
|
||||||
|
MEM_LARGE_PAGES = 0x20000000
|
||||||
|
|
||||||
|
PAGE_NOACCESS = 0x01
|
||||||
|
PAGE_READONLY = 0x02
|
||||||
|
PAGE_READWRITE = 0x04
|
||||||
|
PAGE_WRITECOPY = 0x08
|
||||||
|
PAGE_EXECUTE_READ = 0x20
|
||||||
|
PAGE_EXECUTE_READWRITE = 0x40
|
||||||
|
PAGE_EXECUTE_WRITECOPY = 0x80
|
||||||
|
)
|
7
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
Normal file
7
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2009 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 windows
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
|
30
vendor/golang.org/x/sys/windows/race.go
generated
vendored
Normal file
30
vendor/golang.org/x/sys/windows/race.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows,race
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const raceenabled = true
|
||||||
|
|
||||||
|
func raceAcquire(addr unsafe.Pointer) {
|
||||||
|
runtime.RaceAcquire(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||||
|
runtime.RaceReleaseMerge(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||||
|
runtime.RaceReadRange(addr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||||
|
runtime.RaceWriteRange(addr, len)
|
||||||
|
}
|
25
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
Normal file
25
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows,!race
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const raceenabled = false
|
||||||
|
|
||||||
|
func raceAcquire(addr unsafe.Pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||||
|
}
|
200
vendor/golang.org/x/sys/windows/registry/key.go
generated
vendored
Normal file
200
vendor/golang.org/x/sys/windows/registry/key.go
generated
vendored
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package registry provides access to the Windows registry.
|
||||||
|
//
|
||||||
|
// Here is a simple example, opening a registry key and reading a string value from it.
|
||||||
|
//
|
||||||
|
// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// defer k.Close()
|
||||||
|
//
|
||||||
|
// s, _, err := k.GetStringValue("SystemRoot")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Windows system root is %q\n", s)
|
||||||
|
//
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Registry key security and access rights.
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx
|
||||||
|
// for details.
|
||||||
|
ALL_ACCESS = 0xf003f
|
||||||
|
CREATE_LINK = 0x00020
|
||||||
|
CREATE_SUB_KEY = 0x00004
|
||||||
|
ENUMERATE_SUB_KEYS = 0x00008
|
||||||
|
EXECUTE = 0x20019
|
||||||
|
NOTIFY = 0x00010
|
||||||
|
QUERY_VALUE = 0x00001
|
||||||
|
READ = 0x20019
|
||||||
|
SET_VALUE = 0x00002
|
||||||
|
WOW64_32KEY = 0x00200
|
||||||
|
WOW64_64KEY = 0x00100
|
||||||
|
WRITE = 0x20006
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key is a handle to an open Windows registry key.
|
||||||
|
// Keys can be obtained by calling OpenKey; there are
|
||||||
|
// also some predefined root keys such as CURRENT_USER.
|
||||||
|
// Keys can be used directly in the Windows API.
|
||||||
|
type Key syscall.Handle
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Windows defines some predefined root keys that are always open.
|
||||||
|
// An application can use these keys as entry points to the registry.
|
||||||
|
// Normally these keys are used in OpenKey to open new keys,
|
||||||
|
// but they can also be used anywhere a Key is required.
|
||||||
|
CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT)
|
||||||
|
CURRENT_USER = Key(syscall.HKEY_CURRENT_USER)
|
||||||
|
LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE)
|
||||||
|
USERS = Key(syscall.HKEY_USERS)
|
||||||
|
CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
|
||||||
|
PERFORMANCE_DATA = Key(syscall.HKEY_PERFORMANCE_DATA)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Close closes open key k.
|
||||||
|
func (k Key) Close() error {
|
||||||
|
return syscall.RegCloseKey(syscall.Handle(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenKey opens a new key with path name relative to key k.
|
||||||
|
// It accepts any open key, including CURRENT_USER and others,
|
||||||
|
// and returns the new key and an error.
|
||||||
|
// The access parameter specifies desired access rights to the
|
||||||
|
// key to be opened.
|
||||||
|
func OpenKey(k Key, path string, access uint32) (Key, error) {
|
||||||
|
p, err := syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var subkey syscall.Handle
|
||||||
|
err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Key(subkey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenRemoteKey opens a predefined registry key on another
|
||||||
|
// computer pcname. The key to be opened is specified by k, but
|
||||||
|
// can only be one of LOCAL_MACHINE, PERFORMANCE_DATA or USERS.
|
||||||
|
// If pcname is "", OpenRemoteKey returns local computer key.
|
||||||
|
func OpenRemoteKey(pcname string, k Key) (Key, error) {
|
||||||
|
var err error
|
||||||
|
var p *uint16
|
||||||
|
if pcname != "" {
|
||||||
|
p, err = syscall.UTF16PtrFromString(`\\` + pcname)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remoteKey syscall.Handle
|
||||||
|
err = regConnectRegistry(p, syscall.Handle(k), &remoteKey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Key(remoteKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSubKeyNames returns the names of subkeys of key k.
|
||||||
|
// The parameter n controls the number of returned names,
|
||||||
|
// analogous to the way os.File.Readdirnames works.
|
||||||
|
func (k Key) ReadSubKeyNames(n int) ([]string, error) {
|
||||||
|
ki, err := k.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names := make([]string, 0, ki.SubKeyCount)
|
||||||
|
buf := make([]uint16, ki.MaxSubKeyLen+1) // extra room for terminating zero byte
|
||||||
|
loopItems:
|
||||||
|
for i := uint32(0); ; i++ {
|
||||||
|
if n > 0 {
|
||||||
|
if len(names) == n {
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// Double buffer size and try again.
|
||||||
|
l = uint32(2 * len(buf))
|
||||||
|
buf = make([]uint16, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == _ERROR_NO_MORE_ITEMS {
|
||||||
|
break loopItems
|
||||||
|
}
|
||||||
|
return names, err
|
||||||
|
}
|
||||||
|
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||||
|
}
|
||||||
|
if n > len(names) {
|
||||||
|
return names, io.EOF
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKey creates a key named path under open key k.
|
||||||
|
// CreateKey returns the new key and a boolean flag that reports
|
||||||
|
// whether the key already existed.
|
||||||
|
// The access parameter specifies the access rights for the key
|
||||||
|
// to be created.
|
||||||
|
func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
|
||||||
|
var h syscall.Handle
|
||||||
|
var d uint32
|
||||||
|
err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
|
||||||
|
0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKey deletes the subkey path of key k and its values.
|
||||||
|
func DeleteKey(k Key, path string) error {
|
||||||
|
return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A KeyInfo describes the statistics of a key. It is returned by Stat.
|
||||||
|
type KeyInfo struct {
|
||||||
|
SubKeyCount uint32
|
||||||
|
MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte
|
||||||
|
ValueCount uint32
|
||||||
|
MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
|
||||||
|
MaxValueLen uint32 // longest data component among the key's values, in bytes
|
||||||
|
lastWriteTime syscall.Filetime
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModTime returns the key's last write time.
|
||||||
|
func (ki *KeyInfo) ModTime() time.Time {
|
||||||
|
return time.Unix(0, ki.lastWriteTime.Nanoseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat retrieves information about the open key k.
|
||||||
|
func (k Key) Stat() (*KeyInfo, error) {
|
||||||
|
var ki KeyInfo
|
||||||
|
err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
|
||||||
|
&ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
|
||||||
|
&ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ki, nil
|
||||||
|
}
|
7
vendor/golang.org/x/sys/windows/registry/mksyscall.go
generated
vendored
Normal file
7
vendor/golang.org/x/sys/windows/registry/mksyscall.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2015 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 registry
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
|
32
vendor/golang.org/x/sys/windows/registry/syscall.go
generated
vendored
Normal file
32
vendor/golang.org/x/sys/windows/registry/syscall.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_REG_OPTION_NON_VOLATILE = 0
|
||||||
|
|
||||||
|
_REG_CREATED_NEW_KEY = 1
|
||||||
|
_REG_OPENED_EXISTING_KEY = 2
|
||||||
|
|
||||||
|
_ERROR_NO_MORE_ITEMS syscall.Errno = 259
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadRegLoadMUIString() error {
|
||||||
|
return procRegLoadMUIStringW.Find()
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
|
||||||
|
//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW
|
||||||
|
//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW
|
||||||
|
//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
|
||||||
|
//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
|
||||||
|
//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW
|
||||||
|
//sys regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW
|
||||||
|
|
||||||
|
//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
|
384
vendor/golang.org/x/sys/windows/registry/value.go
generated
vendored
Normal file
384
vendor/golang.org/x/sys/windows/registry/value.go
generated
vendored
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Registry value types.
|
||||||
|
NONE = 0
|
||||||
|
SZ = 1
|
||||||
|
EXPAND_SZ = 2
|
||||||
|
BINARY = 3
|
||||||
|
DWORD = 4
|
||||||
|
DWORD_BIG_ENDIAN = 5
|
||||||
|
LINK = 6
|
||||||
|
MULTI_SZ = 7
|
||||||
|
RESOURCE_LIST = 8
|
||||||
|
FULL_RESOURCE_DESCRIPTOR = 9
|
||||||
|
RESOURCE_REQUIREMENTS_LIST = 10
|
||||||
|
QWORD = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrShortBuffer is returned when the buffer was too short for the operation.
|
||||||
|
ErrShortBuffer = syscall.ERROR_MORE_DATA
|
||||||
|
|
||||||
|
// ErrNotExist is returned when a registry key or value does not exist.
|
||||||
|
ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
|
||||||
|
|
||||||
|
// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
|
||||||
|
ErrUnexpectedType = errors.New("unexpected key value type")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetValue retrieves the type and data for the specified value associated
|
||||||
|
// with an open key k. It fills up buffer buf and returns the retrieved
|
||||||
|
// byte count n. If buf is too small to fit the stored value it returns
|
||||||
|
// ErrShortBuffer error along with the required buffer size n.
|
||||||
|
// If no buffer is provided, it returns true and actual buffer size n.
|
||||||
|
// If no buffer is provided, GetValue returns the value's type only.
|
||||||
|
// If the value does not exist, the error returned is ErrNotExist.
|
||||||
|
//
|
||||||
|
// GetValue is a low level function. If value's type is known, use the appropriate
|
||||||
|
// Get*Value function instead.
|
||||||
|
func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
|
||||||
|
pname, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
var pbuf *byte
|
||||||
|
if len(buf) > 0 {
|
||||||
|
pbuf = (*byte)(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
|
||||||
|
if err != nil {
|
||||||
|
return int(l), valtype, err
|
||||||
|
}
|
||||||
|
return int(l), valtype, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) {
|
||||||
|
p, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
var t uint32
|
||||||
|
n := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
|
||||||
|
if err == nil {
|
||||||
|
return buf[:n], t, nil
|
||||||
|
}
|
||||||
|
if err != syscall.ERROR_MORE_DATA {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(buf)) {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
buf = make([]byte, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringValue retrieves the string value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetStringValue returns ErrNotExist.
|
||||||
|
// If value is not SZ or EXPAND_SZ, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return "", typ, err2
|
||||||
|
}
|
||||||
|
switch typ {
|
||||||
|
case SZ, EXPAND_SZ:
|
||||||
|
default:
|
||||||
|
return "", typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return "", typ, nil
|
||||||
|
}
|
||||||
|
u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:]
|
||||||
|
return syscall.UTF16ToString(u), typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMUIStringValue retrieves the localized string value for
|
||||||
|
// the specified value name associated with an open key k.
|
||||||
|
// If the value name doesn't exist or the localized string value
|
||||||
|
// can't be resolved, GetMUIStringValue returns ErrNotExist.
|
||||||
|
// GetMUIStringValue panics if the system doesn't support
|
||||||
|
// regLoadMUIString; use LoadRegLoadMUIString to check if
|
||||||
|
// regLoadMUIString is supported before calling this function.
|
||||||
|
func (k Key) GetMUIStringValue(name string) (string, error) {
|
||||||
|
pname, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]uint16, 1024)
|
||||||
|
var buflen uint32
|
||||||
|
var pdir *uint16
|
||||||
|
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
|
||||||
|
|
||||||
|
// Try to resolve the string value using the system directory as
|
||||||
|
// a DLL search path; this assumes the string value is of the form
|
||||||
|
// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.
|
||||||
|
|
||||||
|
// This approach works with tzres.dll but may have to be revised
|
||||||
|
// in the future to allow callers to provide custom search paths.
|
||||||
|
|
||||||
|
var s string
|
||||||
|
s, err = ExpandString("%SystemRoot%\\system32\\")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
pdir, err = syscall.UTF16PtrFromString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
|
||||||
|
if buflen <= uint32(len(buf)) {
|
||||||
|
break // Buffer not growing, assume race; break
|
||||||
|
}
|
||||||
|
buf = make([]uint16, buflen)
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandString expands environment-variable strings and replaces
|
||||||
|
// them with the values defined for the current user.
|
||||||
|
// Use ExpandString to expand EXPAND_SZ strings.
|
||||||
|
func ExpandString(value string) (string, error) {
|
||||||
|
if value == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
p, err := syscall.UTF16PtrFromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
r := make([]uint16, 100)
|
||||||
|
for {
|
||||||
|
n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(r)) {
|
||||||
|
u := (*[1 << 29]uint16)(unsafe.Pointer(&r[0]))[:]
|
||||||
|
return syscall.UTF16ToString(u), nil
|
||||||
|
}
|
||||||
|
r = make([]uint16, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringsValue retrieves the []string value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetStringsValue returns ErrNotExist.
|
||||||
|
// If value is not MULTI_SZ, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, typ, err2
|
||||||
|
}
|
||||||
|
if typ != MULTI_SZ {
|
||||||
|
return nil, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil, typ, nil
|
||||||
|
}
|
||||||
|
p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil, typ, nil
|
||||||
|
}
|
||||||
|
if p[len(p)-1] == 0 {
|
||||||
|
p = p[:len(p)-1] // remove terminating null
|
||||||
|
}
|
||||||
|
val = make([]string, 0, 5)
|
||||||
|
from := 0
|
||||||
|
for i, c := range p {
|
||||||
|
if c == 0 {
|
||||||
|
val = append(val, string(utf16.Decode(p[from:i])))
|
||||||
|
from = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntegerValue retrieves the integer value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetIntegerValue returns ErrNotExist.
|
||||||
|
// If value is not DWORD or QWORD, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 8))
|
||||||
|
if err2 != nil {
|
||||||
|
return 0, typ, err2
|
||||||
|
}
|
||||||
|
switch typ {
|
||||||
|
case DWORD:
|
||||||
|
if len(data) != 4 {
|
||||||
|
return 0, typ, errors.New("DWORD value is not 4 bytes long")
|
||||||
|
}
|
||||||
|
return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil
|
||||||
|
case QWORD:
|
||||||
|
if len(data) != 8 {
|
||||||
|
return 0, typ, errors.New("QWORD value is not 8 bytes long")
|
||||||
|
}
|
||||||
|
return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil
|
||||||
|
default:
|
||||||
|
return 0, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBinaryValue retrieves the binary value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetBinaryValue returns ErrNotExist.
|
||||||
|
// If value is not BINARY, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, typ, err2
|
||||||
|
}
|
||||||
|
if typ != BINARY {
|
||||||
|
return nil, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
return data, typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) setValue(name string, valtype uint32, data []byte) error {
|
||||||
|
p, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
|
||||||
|
}
|
||||||
|
return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDWordValue sets the data and type of a name value
|
||||||
|
// under key k to value and DWORD.
|
||||||
|
func (k Key) SetDWordValue(name string, value uint32) error {
|
||||||
|
return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQWordValue sets the data and type of a name value
|
||||||
|
// under key k to value and QWORD.
|
||||||
|
func (k Key) SetQWordValue(name string, value uint64) error {
|
||||||
|
return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) setStringValue(name string, valtype uint32, value string) error {
|
||||||
|
v, err := syscall.UTF16FromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
|
||||||
|
return k.setValue(name, valtype, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStringValue sets the data and type of a name value
|
||||||
|
// under key k to value and SZ. The value must not contain a zero byte.
|
||||||
|
func (k Key) SetStringValue(name, value string) error {
|
||||||
|
return k.setStringValue(name, SZ, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpandStringValue sets the data and type of a name value
|
||||||
|
// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
|
||||||
|
func (k Key) SetExpandStringValue(name, value string) error {
|
||||||
|
return k.setStringValue(name, EXPAND_SZ, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStringsValue sets the data and type of a name value
|
||||||
|
// under key k to value and MULTI_SZ. The value strings
|
||||||
|
// must not contain a zero byte.
|
||||||
|
func (k Key) SetStringsValue(name string, value []string) error {
|
||||||
|
ss := ""
|
||||||
|
for _, s := range value {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == 0 {
|
||||||
|
return errors.New("string cannot have 0 inside")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss += s + "\x00"
|
||||||
|
}
|
||||||
|
v := utf16.Encode([]rune(ss + "\x00"))
|
||||||
|
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
|
||||||
|
return k.setValue(name, MULTI_SZ, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBinaryValue sets the data and type of a name value
|
||||||
|
// under key k to value and BINARY.
|
||||||
|
func (k Key) SetBinaryValue(name string, value []byte) error {
|
||||||
|
return k.setValue(name, BINARY, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteValue removes a named value from the key k.
|
||||||
|
func (k Key) DeleteValue(name string) error {
|
||||||
|
return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadValueNames returns the value names of key k.
|
||||||
|
// The parameter n controls the number of returned names,
|
||||||
|
// analogous to the way os.File.Readdirnames works.
|
||||||
|
func (k Key) ReadValueNames(n int) ([]string, error) {
|
||||||
|
ki, err := k.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names := make([]string, 0, ki.ValueCount)
|
||||||
|
buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
|
||||||
|
loopItems:
|
||||||
|
for i := uint32(0); ; i++ {
|
||||||
|
if n > 0 {
|
||||||
|
if len(names) == n {
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// Double buffer size and try again.
|
||||||
|
l = uint32(2 * len(buf))
|
||||||
|
buf = make([]uint16, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == _ERROR_NO_MORE_ITEMS {
|
||||||
|
break loopItems
|
||||||
|
}
|
||||||
|
return names, err
|
||||||
|
}
|
||||||
|
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||||
|
}
|
||||||
|
if n > len(names) {
|
||||||
|
return names, io.EOF
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
120
vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
generated
vendored
Normal file
120
vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
|
||||||
|
procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
|
||||||
|
procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
|
||||||
|
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
|
||||||
|
procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
|
||||||
|
procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW")
|
||||||
|
procRegConnectRegistryW = modadvapi32.NewProc("RegConnectRegistryW")
|
||||||
|
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
|
||||||
|
n = uint32(r0)
|
||||||
|
if n == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
435
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
Normal file
435
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,435 @@
|
||||||
|
// Copyright 2012 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 windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
STANDARD_RIGHTS_REQUIRED = 0xf0000
|
||||||
|
STANDARD_RIGHTS_READ = 0x20000
|
||||||
|
STANDARD_RIGHTS_WRITE = 0x20000
|
||||||
|
STANDARD_RIGHTS_EXECUTE = 0x20000
|
||||||
|
STANDARD_RIGHTS_ALL = 0x1F0000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameUnknown = 0
|
||||||
|
NameFullyQualifiedDN = 1
|
||||||
|
NameSamCompatible = 2
|
||||||
|
NameDisplay = 3
|
||||||
|
NameUniqueId = 6
|
||||||
|
NameCanonical = 7
|
||||||
|
NameUserPrincipal = 8
|
||||||
|
NameCanonicalEx = 9
|
||||||
|
NameServicePrincipal = 10
|
||||||
|
NameDnsDomain = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
// This function returns 1 byte BOOLEAN rather than the 4 byte BOOL.
|
||||||
|
// http://blogs.msdn.com/b/drnick/archive/2007/12/19/windows-and-upn-format-credentials.aspx
|
||||||
|
//sys TranslateName(accName *uint16, accNameFormat uint32, desiredNameFormat uint32, translatedName *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.TranslateNameW
|
||||||
|
//sys GetUserNameEx(nameFormat uint32, nameBuffre *uint16, nSize *uint32) (err error) [failretval&0xff==0] = secur32.GetUserNameExW
|
||||||
|
|
||||||
|
// TranslateAccountName converts a directory service
|
||||||
|
// object name from one format to another.
|
||||||
|
func TranslateAccountName(username string, from, to uint32, initSize int) (string, error) {
|
||||||
|
u, e := UTF16PtrFromString(username)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
e = TranslateName(u, from, to, &b[0], &n)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b[:n]), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
NetSetupUnknownStatus = iota
|
||||||
|
NetSetupUnjoined
|
||||||
|
NetSetupWorkgroupName
|
||||||
|
NetSetupDomainName
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo10 struct {
|
||||||
|
Name *uint16
|
||||||
|
Comment *uint16
|
||||||
|
UsrComment *uint16
|
||||||
|
FullName *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo
|
||||||
|
//sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation
|
||||||
|
//sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
SidTypeUser = 1 + iota
|
||||||
|
SidTypeGroup
|
||||||
|
SidTypeDomain
|
||||||
|
SidTypeAlias
|
||||||
|
SidTypeWellKnownGroup
|
||||||
|
SidTypeDeletedAccount
|
||||||
|
SidTypeInvalid
|
||||||
|
SidTypeUnknown
|
||||||
|
SidTypeComputer
|
||||||
|
SidTypeLabel
|
||||||
|
)
|
||||||
|
|
||||||
|
type SidIdentifierAuthority struct {
|
||||||
|
Value [6]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SECURITY_NULL_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 0}}
|
||||||
|
SECURITY_WORLD_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 1}}
|
||||||
|
SECURITY_LOCAL_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 2}}
|
||||||
|
SECURITY_CREATOR_SID_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 3}}
|
||||||
|
SECURITY_NON_UNIQUE_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 4}}
|
||||||
|
SECURITY_NT_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 5}}
|
||||||
|
SECURITY_MANDATORY_LABEL_AUTHORITY = SidIdentifierAuthority{[6]byte{0, 0, 0, 0, 0, 16}}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SECURITY_NULL_RID = 0
|
||||||
|
SECURITY_WORLD_RID = 0
|
||||||
|
SECURITY_LOCAL_RID = 0
|
||||||
|
SECURITY_CREATOR_OWNER_RID = 0
|
||||||
|
SECURITY_CREATOR_GROUP_RID = 1
|
||||||
|
SECURITY_DIALUP_RID = 1
|
||||||
|
SECURITY_NETWORK_RID = 2
|
||||||
|
SECURITY_BATCH_RID = 3
|
||||||
|
SECURITY_INTERACTIVE_RID = 4
|
||||||
|
SECURITY_LOGON_IDS_RID = 5
|
||||||
|
SECURITY_SERVICE_RID = 6
|
||||||
|
SECURITY_LOCAL_SYSTEM_RID = 18
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID = 32
|
||||||
|
SECURITY_PRINCIPAL_SELF_RID = 10
|
||||||
|
SECURITY_CREATOR_OWNER_SERVER_RID = 0x2
|
||||||
|
SECURITY_CREATOR_GROUP_SERVER_RID = 0x3
|
||||||
|
SECURITY_LOGON_IDS_RID_COUNT = 0x3
|
||||||
|
SECURITY_ANONYMOUS_LOGON_RID = 0x7
|
||||||
|
SECURITY_PROXY_RID = 0x8
|
||||||
|
SECURITY_ENTERPRISE_CONTROLLERS_RID = 0x9
|
||||||
|
SECURITY_SERVER_LOGON_RID = SECURITY_ENTERPRISE_CONTROLLERS_RID
|
||||||
|
SECURITY_AUTHENTICATED_USER_RID = 0xb
|
||||||
|
SECURITY_RESTRICTED_CODE_RID = 0xc
|
||||||
|
SECURITY_NT_NON_UNIQUE_RID = 0x15
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys LookupAccountSid(systemName *uint16, sid *SID, name *uint16, nameLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) = advapi32.LookupAccountSidW
|
||||||
|
//sys LookupAccountName(systemName *uint16, accountName *uint16, sid *SID, sidLen *uint32, refdDomainName *uint16, refdDomainNameLen *uint32, use *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys ConvertSidToStringSid(sid *SID, stringSid **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys ConvertStringSidToSid(stringSid *uint16, sid **SID) (err error) = advapi32.ConvertStringSidToSidW
|
||||||
|
//sys GetLengthSid(sid *SID) (len uint32) = advapi32.GetLengthSid
|
||||||
|
//sys CopySid(destSidLen uint32, destSid *SID, srcSid *SID) (err error) = advapi32.CopySid
|
||||||
|
//sys AllocateAndInitializeSid(identAuth *SidIdentifierAuthority, subAuth byte, subAuth0 uint32, subAuth1 uint32, subAuth2 uint32, subAuth3 uint32, subAuth4 uint32, subAuth5 uint32, subAuth6 uint32, subAuth7 uint32, sid **SID) (err error) = advapi32.AllocateAndInitializeSid
|
||||||
|
//sys FreeSid(sid *SID) (err error) [failretval!=0] = advapi32.FreeSid
|
||||||
|
//sys EqualSid(sid1 *SID, sid2 *SID) (isEqual bool) = advapi32.EqualSid
|
||||||
|
|
||||||
|
// The security identifier (SID) structure is a variable-length
|
||||||
|
// structure used to uniquely identify users or groups.
|
||||||
|
type SID struct{}
|
||||||
|
|
||||||
|
// StringToSid converts a string-format security identifier
|
||||||
|
// sid into a valid, functional sid.
|
||||||
|
func StringToSid(s string) (*SID, error) {
|
||||||
|
var sid *SID
|
||||||
|
p, e := UTF16PtrFromString(s)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
e = ConvertStringSidToSid(p, &sid)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
defer LocalFree((Handle)(unsafe.Pointer(sid)))
|
||||||
|
return sid.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSID retrieves a security identifier sid for the account
|
||||||
|
// and the name of the domain on which the account was found.
|
||||||
|
// System specify target computer to search.
|
||||||
|
func LookupSID(system, account string) (sid *SID, domain string, accType uint32, err error) {
|
||||||
|
if len(account) == 0 {
|
||||||
|
return nil, "", 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
acc, e := UTF16PtrFromString(account)
|
||||||
|
if e != nil {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
var sys *uint16
|
||||||
|
if len(system) > 0 {
|
||||||
|
sys, e = UTF16PtrFromString(system)
|
||||||
|
if e != nil {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
dn := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
db := make([]uint16, dn)
|
||||||
|
sid = (*SID)(unsafe.Pointer(&b[0]))
|
||||||
|
e = LookupAccountName(sys, acc, sid, &n, &db[0], &dn, &accType)
|
||||||
|
if e == nil {
|
||||||
|
return sid, UTF16ToString(db), accType, nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return nil, "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String converts sid to a string format
|
||||||
|
// suitable for display, storage, or transmission.
|
||||||
|
func (sid *SID) String() (string, error) {
|
||||||
|
var s *uint16
|
||||||
|
e := ConvertSidToStringSid(sid, &s)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
defer LocalFree((Handle)(unsafe.Pointer(s)))
|
||||||
|
return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length, in bytes, of a valid security identifier sid.
|
||||||
|
func (sid *SID) Len() int {
|
||||||
|
return int(GetLengthSid(sid))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a duplicate of security identifier sid.
|
||||||
|
func (sid *SID) Copy() (*SID, error) {
|
||||||
|
b := make([]byte, sid.Len())
|
||||||
|
sid2 := (*SID)(unsafe.Pointer(&b[0]))
|
||||||
|
e := CopySid(uint32(len(b)), sid2, sid)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return sid2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupAccount retrieves the name of the account for this sid
|
||||||
|
// and the name of the first domain on which this sid is found.
|
||||||
|
// System specify target computer to search for.
|
||||||
|
func (sid *SID) LookupAccount(system string) (account, domain string, accType uint32, err error) {
|
||||||
|
var sys *uint16
|
||||||
|
if len(system) > 0 {
|
||||||
|
sys, err = UTF16PtrFromString(system)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := uint32(50)
|
||||||
|
dn := uint32(50)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
db := make([]uint16, dn)
|
||||||
|
e := LookupAccountSid(sys, sid, &b[0], &n, &db[0], &dn, &accType)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b), UTF16ToString(db), accType, nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", "", 0, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", "", 0, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
TOKEN_ASSIGN_PRIMARY = 1 << iota
|
||||||
|
TOKEN_DUPLICATE
|
||||||
|
TOKEN_IMPERSONATE
|
||||||
|
TOKEN_QUERY
|
||||||
|
TOKEN_QUERY_SOURCE
|
||||||
|
TOKEN_ADJUST_PRIVILEGES
|
||||||
|
TOKEN_ADJUST_GROUPS
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
|
||||||
|
TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
|
||||||
|
TOKEN_ASSIGN_PRIMARY |
|
||||||
|
TOKEN_DUPLICATE |
|
||||||
|
TOKEN_IMPERSONATE |
|
||||||
|
TOKEN_QUERY |
|
||||||
|
TOKEN_QUERY_SOURCE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY
|
||||||
|
TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
|
||||||
|
TOKEN_ADJUST_PRIVILEGES |
|
||||||
|
TOKEN_ADJUST_GROUPS |
|
||||||
|
TOKEN_ADJUST_DEFAULT
|
||||||
|
TOKEN_EXECUTE = STANDARD_RIGHTS_EXECUTE
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// do not reorder
|
||||||
|
TokenUser = 1 + iota
|
||||||
|
TokenGroups
|
||||||
|
TokenPrivileges
|
||||||
|
TokenOwner
|
||||||
|
TokenPrimaryGroup
|
||||||
|
TokenDefaultDacl
|
||||||
|
TokenSource
|
||||||
|
TokenType
|
||||||
|
TokenImpersonationLevel
|
||||||
|
TokenStatistics
|
||||||
|
TokenRestrictedSids
|
||||||
|
TokenSessionId
|
||||||
|
TokenGroupsAndPrivileges
|
||||||
|
TokenSessionReference
|
||||||
|
TokenSandBoxInert
|
||||||
|
TokenAuditPolicy
|
||||||
|
TokenOrigin
|
||||||
|
TokenElevationType
|
||||||
|
TokenLinkedToken
|
||||||
|
TokenElevation
|
||||||
|
TokenHasRestrictions
|
||||||
|
TokenAccessInformation
|
||||||
|
TokenVirtualizationAllowed
|
||||||
|
TokenVirtualizationEnabled
|
||||||
|
TokenIntegrityLevel
|
||||||
|
TokenUIAccess
|
||||||
|
TokenMandatoryPolicy
|
||||||
|
TokenLogonSid
|
||||||
|
MaxTokenInfoClass
|
||||||
|
)
|
||||||
|
|
||||||
|
type SIDAndAttributes struct {
|
||||||
|
Sid *SID
|
||||||
|
Attributes uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokenuser struct {
|
||||||
|
User SIDAndAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokenprimarygroup struct {
|
||||||
|
PrimaryGroup *SID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokengroups struct {
|
||||||
|
GroupCount uint32
|
||||||
|
Groups [1]SIDAndAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys OpenProcessToken(h Handle, access uint32, token *Token) (err error) = advapi32.OpenProcessToken
|
||||||
|
//sys GetTokenInformation(t Token, infoClass uint32, info *byte, infoLen uint32, returnedLen *uint32) (err error) = advapi32.GetTokenInformation
|
||||||
|
//sys GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
|
||||||
|
|
||||||
|
// An access token contains the security information for a logon session.
|
||||||
|
// The system creates an access token when a user logs on, and every
|
||||||
|
// process executed on behalf of the user has a copy of the token.
|
||||||
|
// The token identifies the user, the user's groups, and the user's
|
||||||
|
// privileges. The system uses the token to control access to securable
|
||||||
|
// objects and to control the ability of the user to perform various
|
||||||
|
// system-related operations on the local computer.
|
||||||
|
type Token Handle
|
||||||
|
|
||||||
|
// OpenCurrentProcessToken opens the access token
|
||||||
|
// associated with current process.
|
||||||
|
func OpenCurrentProcessToken() (Token, error) {
|
||||||
|
p, e := GetCurrentProcess()
|
||||||
|
if e != nil {
|
||||||
|
return 0, e
|
||||||
|
}
|
||||||
|
var t Token
|
||||||
|
e = OpenProcessToken(p, TOKEN_QUERY, &t)
|
||||||
|
if e != nil {
|
||||||
|
return 0, e
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close releases access to access token.
|
||||||
|
func (t Token) Close() error {
|
||||||
|
return CloseHandle(Handle(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getInfo retrieves a specified type of information about an access token.
|
||||||
|
func (t Token) getInfo(class uint32, initSize int) (unsafe.Pointer, error) {
|
||||||
|
n := uint32(initSize)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
e := GetTokenInformation(t, class, &b[0], uint32(len(b)), &n)
|
||||||
|
if e == nil {
|
||||||
|
return unsafe.Pointer(&b[0]), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenUser retrieves access token t user account information.
|
||||||
|
func (t Token) GetTokenUser() (*Tokenuser, error) {
|
||||||
|
i, e := t.getInfo(TokenUser, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokenuser)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenGroups retrieves group accounts associated with access token t.
|
||||||
|
func (t Token) GetTokenGroups() (*Tokengroups, error) {
|
||||||
|
i, e := t.getInfo(TokenGroups, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokengroups)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenPrimaryGroup retrieves access token t primary group information.
|
||||||
|
// A pointer to a SID structure representing a group that will become
|
||||||
|
// the primary group of any objects created by a process using this access token.
|
||||||
|
func (t Token) GetTokenPrimaryGroup() (*Tokenprimarygroup, error) {
|
||||||
|
i, e := t.getInfo(TokenPrimaryGroup, 50)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return (*Tokenprimarygroup)(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserProfileDirectory retrieves path to the
|
||||||
|
// root directory of the access token t user's profile.
|
||||||
|
func (t Token) GetUserProfileDirectory() (string, error) {
|
||||||
|
n := uint32(100)
|
||||||
|
for {
|
||||||
|
b := make([]uint16, n)
|
||||||
|
e := GetUserProfileDirectory(t, &b[0], &n)
|
||||||
|
if e == nil {
|
||||||
|
return UTF16ToString(b), nil
|
||||||
|
}
|
||||||
|
if e != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
164
vendor/golang.org/x/sys/windows/service.go
generated
vendored
Normal file
164
vendor/golang.org/x/sys/windows/service.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
const (
|
||||||
|
SC_MANAGER_CONNECT = 1
|
||||||
|
SC_MANAGER_CREATE_SERVICE = 2
|
||||||
|
SC_MANAGER_ENUMERATE_SERVICE = 4
|
||||||
|
SC_MANAGER_LOCK = 8
|
||||||
|
SC_MANAGER_QUERY_LOCK_STATUS = 16
|
||||||
|
SC_MANAGER_MODIFY_BOOT_CONFIG = 32
|
||||||
|
SC_MANAGER_ALL_ACCESS = 0xf003f
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
|
||||||
|
|
||||||
|
const (
|
||||||
|
SERVICE_KERNEL_DRIVER = 1
|
||||||
|
SERVICE_FILE_SYSTEM_DRIVER = 2
|
||||||
|
SERVICE_ADAPTER = 4
|
||||||
|
SERVICE_RECOGNIZER_DRIVER = 8
|
||||||
|
SERVICE_WIN32_OWN_PROCESS = 16
|
||||||
|
SERVICE_WIN32_SHARE_PROCESS = 32
|
||||||
|
SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS
|
||||||
|
SERVICE_INTERACTIVE_PROCESS = 256
|
||||||
|
SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER
|
||||||
|
SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS
|
||||||
|
|
||||||
|
SERVICE_BOOT_START = 0
|
||||||
|
SERVICE_SYSTEM_START = 1
|
||||||
|
SERVICE_AUTO_START = 2
|
||||||
|
SERVICE_DEMAND_START = 3
|
||||||
|
SERVICE_DISABLED = 4
|
||||||
|
|
||||||
|
SERVICE_ERROR_IGNORE = 0
|
||||||
|
SERVICE_ERROR_NORMAL = 1
|
||||||
|
SERVICE_ERROR_SEVERE = 2
|
||||||
|
SERVICE_ERROR_CRITICAL = 3
|
||||||
|
|
||||||
|
SC_STATUS_PROCESS_INFO = 0
|
||||||
|
|
||||||
|
SERVICE_STOPPED = 1
|
||||||
|
SERVICE_START_PENDING = 2
|
||||||
|
SERVICE_STOP_PENDING = 3
|
||||||
|
SERVICE_RUNNING = 4
|
||||||
|
SERVICE_CONTINUE_PENDING = 5
|
||||||
|
SERVICE_PAUSE_PENDING = 6
|
||||||
|
SERVICE_PAUSED = 7
|
||||||
|
SERVICE_NO_CHANGE = 0xffffffff
|
||||||
|
|
||||||
|
SERVICE_ACCEPT_STOP = 1
|
||||||
|
SERVICE_ACCEPT_PAUSE_CONTINUE = 2
|
||||||
|
SERVICE_ACCEPT_SHUTDOWN = 4
|
||||||
|
SERVICE_ACCEPT_PARAMCHANGE = 8
|
||||||
|
SERVICE_ACCEPT_NETBINDCHANGE = 16
|
||||||
|
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 32
|
||||||
|
SERVICE_ACCEPT_POWEREVENT = 64
|
||||||
|
SERVICE_ACCEPT_SESSIONCHANGE = 128
|
||||||
|
|
||||||
|
SERVICE_CONTROL_STOP = 1
|
||||||
|
SERVICE_CONTROL_PAUSE = 2
|
||||||
|
SERVICE_CONTROL_CONTINUE = 3
|
||||||
|
SERVICE_CONTROL_INTERROGATE = 4
|
||||||
|
SERVICE_CONTROL_SHUTDOWN = 5
|
||||||
|
SERVICE_CONTROL_PARAMCHANGE = 6
|
||||||
|
SERVICE_CONTROL_NETBINDADD = 7
|
||||||
|
SERVICE_CONTROL_NETBINDREMOVE = 8
|
||||||
|
SERVICE_CONTROL_NETBINDENABLE = 9
|
||||||
|
SERVICE_CONTROL_NETBINDDISABLE = 10
|
||||||
|
SERVICE_CONTROL_DEVICEEVENT = 11
|
||||||
|
SERVICE_CONTROL_HARDWAREPROFILECHANGE = 12
|
||||||
|
SERVICE_CONTROL_POWEREVENT = 13
|
||||||
|
SERVICE_CONTROL_SESSIONCHANGE = 14
|
||||||
|
|
||||||
|
SERVICE_ACTIVE = 1
|
||||||
|
SERVICE_INACTIVE = 2
|
||||||
|
SERVICE_STATE_ALL = 3
|
||||||
|
|
||||||
|
SERVICE_QUERY_CONFIG = 1
|
||||||
|
SERVICE_CHANGE_CONFIG = 2
|
||||||
|
SERVICE_QUERY_STATUS = 4
|
||||||
|
SERVICE_ENUMERATE_DEPENDENTS = 8
|
||||||
|
SERVICE_START = 16
|
||||||
|
SERVICE_STOP = 32
|
||||||
|
SERVICE_PAUSE_CONTINUE = 64
|
||||||
|
SERVICE_INTERROGATE = 128
|
||||||
|
SERVICE_USER_DEFINED_CONTROL = 256
|
||||||
|
SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL
|
||||||
|
SERVICE_RUNS_IN_SYSTEM_PROCESS = 1
|
||||||
|
SERVICE_CONFIG_DESCRIPTION = 1
|
||||||
|
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
||||||
|
|
||||||
|
NO_ERROR = 0
|
||||||
|
|
||||||
|
SC_ENUM_PROCESS_INFO = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type SERVICE_STATUS struct {
|
||||||
|
ServiceType uint32
|
||||||
|
CurrentState uint32
|
||||||
|
ControlsAccepted uint32
|
||||||
|
Win32ExitCode uint32
|
||||||
|
ServiceSpecificExitCode uint32
|
||||||
|
CheckPoint uint32
|
||||||
|
WaitHint uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_TABLE_ENTRY struct {
|
||||||
|
ServiceName *uint16
|
||||||
|
ServiceProc uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type QUERY_SERVICE_CONFIG struct {
|
||||||
|
ServiceType uint32
|
||||||
|
StartType uint32
|
||||||
|
ErrorControl uint32
|
||||||
|
BinaryPathName *uint16
|
||||||
|
LoadOrderGroup *uint16
|
||||||
|
TagId uint32
|
||||||
|
Dependencies *uint16
|
||||||
|
ServiceStartName *uint16
|
||||||
|
DisplayName *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_DESCRIPTION struct {
|
||||||
|
Description *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type SERVICE_STATUS_PROCESS struct {
|
||||||
|
ServiceType uint32
|
||||||
|
CurrentState uint32
|
||||||
|
ControlsAccepted uint32
|
||||||
|
Win32ExitCode uint32
|
||||||
|
ServiceSpecificExitCode uint32
|
||||||
|
CheckPoint uint32
|
||||||
|
WaitHint uint32
|
||||||
|
ProcessId uint32
|
||||||
|
ServiceFlags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ENUM_SERVICE_STATUS_PROCESS struct {
|
||||||
|
ServiceName *uint16
|
||||||
|
DisplayName *uint16
|
||||||
|
ServiceStatusProcess SERVICE_STATUS_PROCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
|
||||||
|
//sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW
|
||||||
|
//sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
|
||||||
|
//sys DeleteService(service Handle) (err error) = advapi32.DeleteService
|
||||||
|
//sys StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
|
||||||
|
//sys QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus
|
||||||
|
//sys ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService
|
||||||
|
//sys StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
|
||||||
|
//sys SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus
|
||||||
|
//sys ChangeServiceConfig(service Handle, serviceType uint32, startType uint32, errorControl uint32, binaryPathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16, displayName *uint16) (err error) = advapi32.ChangeServiceConfigW
|
||||||
|
//sys QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfigW
|
||||||
|
//sys ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) = advapi32.ChangeServiceConfig2W
|
||||||
|
//sys QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfig2W
|
||||||
|
//sys EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) = advapi32.EnumServicesStatusExW
|
22
vendor/golang.org/x/sys/windows/str.go
generated
vendored
Normal file
22
vendor/golang.org/x/sys/windows/str.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||||
|
if val < 0 {
|
||||||
|
return "-" + itoa(-val)
|
||||||
|
}
|
||||||
|
var buf [32]byte // big enough for int64
|
||||||
|
i := len(buf) - 1
|
||||||
|
for val >= 10 {
|
||||||
|
buf[i] = byte(val%10 + '0')
|
||||||
|
i--
|
||||||
|
val /= 10
|
||||||
|
}
|
||||||
|
buf[i] = byte(val + '0')
|
||||||
|
return string(buf[i:])
|
||||||
|
}
|
80
vendor/golang.org/x/sys/windows/svc/eventlog/install.go
generated
vendored
Normal file
80
vendor/golang.org/x/sys/windows/svc/eventlog/install.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package eventlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Log levels.
|
||||||
|
Info = windows.EVENTLOG_INFORMATION_TYPE
|
||||||
|
Warning = windows.EVENTLOG_WARNING_TYPE
|
||||||
|
Error = windows.EVENTLOG_ERROR_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application`
|
||||||
|
|
||||||
|
// Install modifies PC registry to allow logging with an event source src.
|
||||||
|
// It adds all required keys and values to the event log registry key.
|
||||||
|
// Install uses msgFile as the event message file. If useExpandKey is true,
|
||||||
|
// the event message file is installed as REG_EXPAND_SZ value,
|
||||||
|
// otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and
|
||||||
|
// log.Info to specify events supported by the new event source.
|
||||||
|
func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error {
|
||||||
|
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer appkey.Close()
|
||||||
|
|
||||||
|
sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sk.Close()
|
||||||
|
if alreadyExist {
|
||||||
|
return errors.New(addKeyName + `\` + src + " registry key already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sk.SetDWordValue("CustomSource", 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if useExpandKey {
|
||||||
|
err = sk.SetExpandStringValue("EventMessageFile", msgFile)
|
||||||
|
} else {
|
||||||
|
err = sk.SetStringValue("EventMessageFile", msgFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = sk.SetDWordValue("TypesSupported", eventsSupported)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallAsEventCreate is the same as Install, but uses
|
||||||
|
// %SystemRoot%\System32\EventCreate.exe as the event message file.
|
||||||
|
func InstallAsEventCreate(src string, eventsSupported uint32) error {
|
||||||
|
return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", true, eventsSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove deletes all registry elements installed by the correspondent Install.
|
||||||
|
func Remove(src string) error {
|
||||||
|
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.SET_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer appkey.Close()
|
||||||
|
return registry.DeleteKey(appkey, src)
|
||||||
|
}
|
70
vendor/golang.org/x/sys/windows/svc/eventlog/log.go
generated
vendored
Normal file
70
vendor/golang.org/x/sys/windows/svc/eventlog/log.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2012 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package eventlog implements access to Windows event log.
|
||||||
|
//
|
||||||
|
package eventlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log provides access to the system log.
|
||||||
|
type Log struct {
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open retrieves a handle to the specified event log.
|
||||||
|
func Open(source string) (*Log, error) {
|
||||||
|
return OpenRemote("", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenRemote does the same as Open, but on different computer host.
|
||||||
|
func OpenRemote(host, source string) (*Log, error) {
|
||||||
|
if source == "" {
|
||||||
|
return nil, errors.New("Specify event log source")
|
||||||
|
}
|
||||||
|
var s *uint16
|
||||||
|
if host != "" {
|
||||||
|
s = syscall.StringToUTF16Ptr(host)
|
||||||
|
}
|
||||||
|
h, err := windows.RegisterEventSource(s, syscall.StringToUTF16Ptr(source))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Log{Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes event log l.
|
||||||
|
func (l *Log) Close() error {
|
||||||
|
return windows.DeregisterEventSource(l.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Log) report(etype uint16, eid uint32, msg string) error {
|
||||||
|
ss := []*uint16{syscall.StringToUTF16Ptr(msg)}
|
||||||
|
return windows.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info writes an information event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Info(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_INFORMATION_TYPE, eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning writes an warning event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Warning(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_WARNING_TYPE, eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error writes an error event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Error(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_ERROR_TYPE, eid, msg)
|
||||||
|
}
|
71
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
Normal file
71
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package windows contains an interface to the low-level operating system
|
||||||
|
// primitives. OS details vary depending on the underlying system, and
|
||||||
|
// by default, godoc will display the OS-specific documentation for the current
|
||||||
|
// system. If you want godoc to display syscall documentation for another
|
||||||
|
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||||
|
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||||
|
// to freebsd and $GOARCH to arm.
|
||||||
|
// The primary use of this package is inside other packages that provide a more
|
||||||
|
// portable interface to the system, such as "os", "time" and "net". Use
|
||||||
|
// those packages rather than this one if you can.
|
||||||
|
// For details of the functions and data types in this package consult
|
||||||
|
// the manuals for the appropriate operating system.
|
||||||
|
// These calls return err == nil to indicate success; otherwise
|
||||||
|
// err represents an operating system error describing the failure and
|
||||||
|
// holds a value of type syscall.Errno.
|
||||||
|
package windows // import "golang.org/x/sys/windows"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||||
|
// containing the text of s. If s contains a NUL byte at any
|
||||||
|
// location, it returns (nil, syscall.EINVAL).
|
||||||
|
func ByteSliceFromString(s string) ([]byte, error) {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == 0 {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a := make([]byte, len(s)+1)
|
||||||
|
copy(a, s)
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||||
|
// bytes containing the text of s. If s contains a NUL byte at any
|
||||||
|
// location, it returns (nil, syscall.EINVAL).
|
||||||
|
func BytePtrFromString(s string) (*byte, error) {
|
||||||
|
a, err := ByteSliceFromString(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &a[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||||
|
// See mksyscall.pl.
|
||||||
|
var _zero uintptr
|
||||||
|
|
||||||
|
func (ts *Timespec) Unix() (sec int64, nsec int64) {
|
||||||
|
return int64(ts.Sec), int64(ts.Nsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tv *Timeval) Unix() (sec int64, nsec int64) {
|
||||||
|
return int64(tv.Sec), int64(tv.Usec) * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *Timespec) Nano() int64 {
|
||||||
|
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tv *Timeval) Nano() int64 {
|
||||||
|
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
|
||||||
|
}
|
1004
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
Normal file
1004
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1282
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
Normal file
1282
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
22
vendor/golang.org/x/sys/windows/types_windows_386.go
generated
vendored
Normal file
22
vendor/golang.org/x/sys/windows/types_windows_386.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// 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 windows
|
||||||
|
|
||||||
|
type WSAData struct {
|
||||||
|
Version uint16
|
||||||
|
HighVersion uint16
|
||||||
|
Description [WSADESCRIPTION_LEN + 1]byte
|
||||||
|
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||||
|
MaxSockets uint16
|
||||||
|
MaxUdpDg uint16
|
||||||
|
VendorInfo *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servent struct {
|
||||||
|
Name *byte
|
||||||
|
Aliases **byte
|
||||||
|
Port uint16
|
||||||
|
Proto *byte
|
||||||
|
}
|
22
vendor/golang.org/x/sys/windows/types_windows_amd64.go
generated
vendored
Normal file
22
vendor/golang.org/x/sys/windows/types_windows_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// 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 windows
|
||||||
|
|
||||||
|
type WSAData struct {
|
||||||
|
Version uint16
|
||||||
|
HighVersion uint16
|
||||||
|
MaxSockets uint16
|
||||||
|
MaxUdpDg uint16
|
||||||
|
VendorInfo *byte
|
||||||
|
Description [WSADESCRIPTION_LEN + 1]byte
|
||||||
|
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type Servent struct {
|
||||||
|
Name *byte
|
||||||
|
Aliases **byte
|
||||||
|
Proto *byte
|
||||||
|
Port uint16
|
||||||
|
}
|
2428
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
2428
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
19
vendor/gopkg.in/alecthomas/kingpin.v2/COPYING
generated
vendored
Normal file
19
vendor/gopkg.in/alecthomas/kingpin.v2/COPYING
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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.
|
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
Normal file
674
vendor/gopkg.in/alecthomas/kingpin.v2/README.md
generated
vendored
Normal file
|
@ -0,0 +1,674 @@
|
||||||
|
# Kingpin - A Go (golang) command line and flag parser
|
||||||
|
[](http://godoc.org/github.com/alecthomas/kingpin) [](https://travis-ci.org/alecthomas/kingpin) [](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() []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>
|
||||||
|
```
|
42
vendor/gopkg.in/alecthomas/kingpin.v2/actions.go
generated
vendored
Normal file
42
vendor/gopkg.in/alecthomas/kingpin.v2/actions.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
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
|
||||||
|
}
|
685
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
Normal file
685
vendor/gopkg.in/alecthomas/kingpin.v2/app.go
generated
vendored
Normal file
|
@ -0,0 +1,685 @@
|
||||||
|
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, "_"))
|
||||||
|
}
|
184
vendor/gopkg.in/alecthomas/kingpin.v2/args.go
generated
vendored
Normal file
184
vendor/gopkg.in/alecthomas/kingpin.v2/args.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
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
|
||||||
|
}
|
274
vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go
generated
vendored
Normal file
274
vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go
generated
vendored
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
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
|
||||||
|
}
|
33
vendor/gopkg.in/alecthomas/kingpin.v2/completions.go
generated
vendored
Normal file
33
vendor/gopkg.in/alecthomas/kingpin.v2/completions.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
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
|
||||||
|
}
|
68
vendor/gopkg.in/alecthomas/kingpin.v2/doc.go
generated
vendored
Normal file
68
vendor/gopkg.in/alecthomas/kingpin.v2/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// 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
|
45
vendor/gopkg.in/alecthomas/kingpin.v2/envar.go
generated
vendored
Normal file
45
vendor/gopkg.in/alecthomas/kingpin.v2/envar.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
||||||
|
}
|
308
vendor/gopkg.in/alecthomas/kingpin.v2/flags.go
generated
vendored
Normal file
308
vendor/gopkg.in/alecthomas/kingpin.v2/flags.go
generated
vendored
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
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
|
||||||
|
}
|
94
vendor/gopkg.in/alecthomas/kingpin.v2/global.go
generated
vendored
Normal file
94
vendor/gopkg.in/alecthomas/kingpin.v2/global.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
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)
|
||||||
|
}
|
9
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go
generated
vendored
Normal file
9
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd
|
||||||
|
|
||||||
|
package kingpin
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func guessWidth(w io.Writer) int {
|
||||||
|
return 80
|
||||||
|
}
|
38
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go
generated
vendored
Normal file
38
vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// +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
|
||||||
|
}
|
227
vendor/gopkg.in/alecthomas/kingpin.v2/model.go
generated
vendored
Normal file
227
vendor/gopkg.in/alecthomas/kingpin.v2/model.go
generated
vendored
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
382
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
Normal file
382
vendor/gopkg.in/alecthomas/kingpin.v2/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
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 = expanded
|
||||||
|
} else {
|
||||||
|
p.args = append(expanded, p.args...)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
212
vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go
generated
vendored
Normal file
212
vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
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))
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue