Update git-validation
... from a 6-year-old version Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
parent
e584bbc882
commit
5953c36eee
|
|
@ -1,16 +1,19 @@
|
||||||
module github.com/containers/storage/tests/tools
|
module github.com/containers/storage/tests/tools
|
||||||
|
|
||||||
go 1.18
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cpuguy83/go-md2man v1.0.10
|
github.com/cpuguy83/go-md2man v1.0.10
|
||||||
github.com/vbatts/git-validation v1.0.0
|
github.com/vbatts/git-validation v1.2.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/hashicorp/go-version v1.2.0 // indirect
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
|
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||||
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/russross/blackfriday v1.5.2 // indirect
|
github.com/russross/blackfriday v1.5.2 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.1 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,40 @@
|
||||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||||
|
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
|
||||||
|
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/vbatts/git-validation v1.0.0 h1:Eb7bD3ThOk/2P01M+KaUng0gka9+MVvpn6d0a6AKxq0=
|
github.com/vbatts/git-validation v1.2.2 h1:AHTS8Jara7Pcu0ub7RusMBAvyLtjsJtF/xPU5Pm1BPE=
|
||||||
github.com/vbatts/git-validation v1.0.0/go.mod h1:QyK3uQnRYWGt/5ezd8kcpwPrm6zn9tNM/KtozbpfU6k=
|
github.com/vbatts/git-validation v1.2.2/go.mod h1:Fj+04EdPcZ0rMOR+dqvppMVVgyNcOFRT5iFWuIc593A=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Fatih Arslan
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
# color [](https://github.com/fatih/color/actions) [](https://pkg.go.dev/github.com/fatih/color)
|
||||||
|
|
||||||
|
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||||
|
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||||
|
has support for Windows too! The API can be used in several ways, pick one that
|
||||||
|
suits you.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/fatih/color
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Standard colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Print with default helper functions
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// A newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// These are using the default foreground colors
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### RGB colors
|
||||||
|
|
||||||
|
If your terminal supports 24-bit colors, you can use RGB color codes.
|
||||||
|
|
||||||
|
```go
|
||||||
|
color.RGB(255, 128, 0).Println("foreground orange")
|
||||||
|
color.RGB(230, 42, 42).Println("foreground red")
|
||||||
|
|
||||||
|
color.BgRGB(255, 128, 0).Println("background orange")
|
||||||
|
color.BgRGB(230, 42, 42).Println("background red")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mix and reuse colors
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with white background.")
|
||||||
|
|
||||||
|
// Mix with RGB color codes
|
||||||
|
color.RGB(255, 128, 0).AddBgRGB(0, 0, 0).Println("orange with black background")
|
||||||
|
|
||||||
|
color.BgRGB(255, 128, 0).AddRGB(255, 255, 255).Println("orange background with white foreground")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use your own output (io.Writer)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(writer, "This will print text in blue.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom print functions (PrintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create a custom print function for convenience
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("Warning")
|
||||||
|
red("Error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom fprint functions (FprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
blue := color.New(color.FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, "Don't forget this...")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Insert into noncolor strings (SprintFunc)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
yellow := color.New(color.FgYellow).SprintFunc()
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("This %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
// Use helper functions
|
||||||
|
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||||
|
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||||
|
|
||||||
|
// Windows supported too! Just don't forget to change the output to color.Output
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Plug into existing code
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Use handy standard colors
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will now be in yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // Don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // Use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will now be bold magenta.")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable/Enable color
|
||||||
|
|
||||||
|
There might be a case where you want to explicitly disable/enable color output. the
|
||||||
|
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||||
|
(for example if the output were piped directly to `less`).
|
||||||
|
|
||||||
|
The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment
|
||||||
|
variable is set to a non-empty string.
|
||||||
|
|
||||||
|
`Color` has support to disable/enable colors programmatically both globally and
|
||||||
|
for single color definitions. For example suppose you have a CLI app and a
|
||||||
|
`-no-color` bool flag. You can easily disable the color output with:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
```
|
||||||
|
|
||||||
|
## GitHub Actions
|
||||||
|
|
||||||
|
To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
|
||||||
|
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Fatih Arslan](https://github.com/fatih)
|
||||||
|
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||||
|
|
@ -0,0 +1,685 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||||
|
// false or true based on the stdout's file descriptor referring to a terminal
|
||||||
|
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||||
|
// set (regardless of its value). This is a global option and affects all
|
||||||
|
// colors. For more control over each color block use the methods
|
||||||
|
// DisableColor() individually.
|
||||||
|
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
|
||||||
|
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||||
|
|
||||||
|
// Output defines the standard output of the print functions. By default,
|
||||||
|
// os.Stdout is used.
|
||||||
|
Output = colorable.NewColorableStdout()
|
||||||
|
|
||||||
|
// Error defines a color supporting writer for os.Stderr.
|
||||||
|
Error = colorable.NewColorableStderr()
|
||||||
|
|
||||||
|
// colorsCache is used to reduce the count of created Color objects and
|
||||||
|
// allows to reuse already created objects with required Attribute.
|
||||||
|
colorsCache = make(map[Attribute]*Color)
|
||||||
|
colorsCacheMu sync.Mutex // protects colorsCache
|
||||||
|
)
|
||||||
|
|
||||||
|
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||||
|
func noColorIsSet() bool {
|
||||||
|
return os.Getenv("NO_COLOR") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color defines a custom color object which is defined by SGR parameters.
|
||||||
|
type Color struct {
|
||||||
|
params []Attribute
|
||||||
|
noColor *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute defines a single SGR Code
|
||||||
|
type Attribute int
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
// Base attributes
|
||||||
|
const (
|
||||||
|
Reset Attribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResetBold Attribute = iota + 22
|
||||||
|
ResetItalic
|
||||||
|
ResetUnderline
|
||||||
|
ResetBlinking
|
||||||
|
_
|
||||||
|
ResetReversed
|
||||||
|
ResetConcealed
|
||||||
|
ResetCrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||||
|
Bold: ResetBold,
|
||||||
|
Faint: ResetBold,
|
||||||
|
Italic: ResetItalic,
|
||||||
|
Underline: ResetUnderline,
|
||||||
|
BlinkSlow: ResetBlinking,
|
||||||
|
BlinkRapid: ResetBlinking,
|
||||||
|
ReverseVideo: ResetReversed,
|
||||||
|
Concealed: ResetConcealed,
|
||||||
|
CrossedOut: ResetCrossedOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
|
||||||
|
// used internally for 256 and 24-bit coloring
|
||||||
|
foreground
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
FgHiBlack Attribute = iota + 90
|
||||||
|
FgHiRed
|
||||||
|
FgHiGreen
|
||||||
|
FgHiYellow
|
||||||
|
FgHiBlue
|
||||||
|
FgHiMagenta
|
||||||
|
FgHiCyan
|
||||||
|
FgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack Attribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
|
||||||
|
// used internally for 256 and 24-bit coloring
|
||||||
|
background
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
BgHiBlack Attribute = iota + 100
|
||||||
|
BgHiRed
|
||||||
|
BgHiGreen
|
||||||
|
BgHiYellow
|
||||||
|
BgHiBlue
|
||||||
|
BgHiMagenta
|
||||||
|
BgHiCyan
|
||||||
|
BgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a newly created color object.
|
||||||
|
func New(value ...Attribute) *Color {
|
||||||
|
c := &Color{
|
||||||
|
params: make([]Attribute, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
if noColorIsSet() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Add(value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGB returns a new foreground color in 24-bit RGB.
|
||||||
|
func RGB(r, g, b int) *Color {
|
||||||
|
return New(foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BgRGB returns a new background color in 24-bit RGB.
|
||||||
|
func BgRGB(r, g, b int) *Color {
|
||||||
|
return New(background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRGB is used to chain foreground RGB SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||||
|
func (c *Color) AddRGB(r, g, b int) *Color {
|
||||||
|
c.params = append(c.params, foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRGB is used to chain background RGB SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||||
|
func (c *Color) AddBgRGB(r, g, b int) *Color {
|
||||||
|
c.params = append(c.params, background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the given parameters immediately. It will change the color of
|
||||||
|
// output with the given SGR parameters until color.Unset() is called.
|
||||||
|
func Set(p ...Attribute) *Color {
|
||||||
|
c := New(p...)
|
||||||
|
c.Set()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset resets all escape attributes and clears the output. Usually should
|
||||||
|
// be called after Set().
|
||||||
|
func Unset() {
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the SGR sequence.
|
||||||
|
func (c *Color) Set() *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(Output, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unset() {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||||
|
// a low-level function, and users should use the higher-level functions, such
|
||||||
|
// as color.Fprint, color.Print, etc.
|
||||||
|
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||||
|
// io.Writer. Usually should be called after SetWriter().
|
||||||
|
func (c *Color) UnsetWriter(w io.Writer) {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if NoColor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||||
|
func (c *Color) Add(value ...Attribute) *Color {
|
||||||
|
c.params = append(c.params, value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprint formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are added between operands when neither is a string.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
c.SetWriter(w)
|
||||||
|
defer c.UnsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are added between operands when neither is a
|
||||||
|
// string. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprint(Output, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintf formats according to a format specifier and writes to w.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.SetWriter(w)
|
||||||
|
defer c.UnsetWriter(w)
|
||||||
|
|
||||||
|
return fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf formats according to a format specifier and writes to standard output.
|
||||||
|
// It returns the number of bytes written and any write error encountered.
|
||||||
|
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||||
|
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||||
|
c.Set()
|
||||||
|
defer c.unset()
|
||||||
|
|
||||||
|
return fmt.Fprintf(Output, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fprintln formats using the default formats for its operands and writes to w.
|
||||||
|
// Spaces are always added between operands and a newline is appended.
|
||||||
|
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||||
|
// type *os.File.
|
||||||
|
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(w, c.wrap(sprintln(a...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println formats using the default formats for its operands and writes to
|
||||||
|
// standard output. Spaces are always added between operands and a newline is
|
||||||
|
// appended. It returns the number of bytes written and any write error
|
||||||
|
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||||
|
// color.
|
||||||
|
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||||
|
return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is just like Print, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprint(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintln(a ...interface{}) string {
|
||||||
|
return c.wrap(sprintln(a...)) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprint().
|
||||||
|
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Print().
|
||||||
|
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Print(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintf().
|
||||||
|
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
return func(w io.Writer, format string, a ...interface{}) {
|
||||||
|
c.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintfFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Printf().
|
||||||
|
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||||
|
return func(format string, a ...interface{}) {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Fprintln().
|
||||||
|
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||||
|
return func(w io.Writer, a ...interface{}) {
|
||||||
|
c.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||||
|
// colorized with color.Println().
|
||||||
|
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||||
|
return func(a ...interface{}) {
|
||||||
|
c.Println(a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output, example:
|
||||||
|
//
|
||||||
|
// put := New(FgYellow).SprintFunc()
|
||||||
|
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||||
|
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintfFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||||
|
return func(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||||
|
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||||
|
// string. Windows users should use this in conjunction with color.Output.
|
||||||
|
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||||
|
return func(a ...interface{}) string {
|
||||||
|
return c.wrap(sprintln(a...)) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||||
|
// an example output might be: "1;36" -> bold cyan
|
||||||
|
func (c *Color) sequence() string {
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(format, ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||||
|
// be printed.
|
||||||
|
func (c *Color) wrap(s string) string {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.format() + s + c.unformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) format() string {
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unformat() string {
|
||||||
|
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
|
//for each element in sequence let's use the specific reset escape, or the generic one if not found
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(Reset))
|
||||||
|
ra, ok := mapResetAttributes[v]
|
||||||
|
if ok {
|
||||||
|
format[i] = strconv.Itoa(int(ra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableColor disables the color output. Useful to not change any existing
|
||||||
|
// code and still being able to output. Can be used for flags like
|
||||||
|
// "--no-color". To enable back use EnableColor() method.
|
||||||
|
func (c *Color) DisableColor() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColor enables the color output. Use it in conjunction with
|
||||||
|
// DisableColor(). Otherwise, this method has no side effects.
|
||||||
|
func (c *Color) EnableColor() {
|
||||||
|
c.noColor = boolPtr(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) isNoColorSet() bool {
|
||||||
|
// check first if we have user set action
|
||||||
|
if c.noColor != nil {
|
||||||
|
return *c.noColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not return the global option, which is disabled by default
|
||||||
|
return NoColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns a boolean value indicating whether two colors are equal.
|
||||||
|
func (c *Color) Equals(c2 *Color) bool {
|
||||||
|
if c == nil && c2 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c == nil || c2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(c.params) != len(c2.params) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if !c2.attrExists(attr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) attrExists(a Attribute) bool {
|
||||||
|
for _, attr := range c.params {
|
||||||
|
if attr == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolPtr(v bool) *bool {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCachedColor(p Attribute) *Color {
|
||||||
|
colorsCacheMu.Lock()
|
||||||
|
defer colorsCacheMu.Unlock()
|
||||||
|
|
||||||
|
c, ok := colorsCache[p]
|
||||||
|
if !ok {
|
||||||
|
c = New(p)
|
||||||
|
colorsCache[p] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(format, "\n") {
|
||||||
|
format += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
c.Print(format)
|
||||||
|
} else {
|
||||||
|
c.Printf(format, a...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||||
|
c := getCachedColor(p)
|
||||||
|
|
||||||
|
if len(a) == 0 {
|
||||||
|
return c.SprintFunc()(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SprintfFunc()(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Black is a convenient helper function to print with black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// Red is a convenient helper function to print with red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// Green is a convenient helper function to print with green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// Yellow is a convenient helper function to print with yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// Blue is a convenient helper function to print with blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// Magenta is a convenient helper function to print with magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||||
|
|
||||||
|
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// White is a convenient helper function to print with white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// BlackString is a convenient helper function to return a string with black
|
||||||
|
// foreground.
|
||||||
|
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// RedString is a convenient helper function to return a string with red
|
||||||
|
// foreground.
|
||||||
|
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// GreenString is a convenient helper function to return a string with green
|
||||||
|
// foreground.
|
||||||
|
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// YellowString is a convenient helper function to return a string with yellow
|
||||||
|
// foreground.
|
||||||
|
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// BlueString is a convenient helper function to return a string with blue
|
||||||
|
// foreground.
|
||||||
|
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// MagentaString is a convenient helper function to return a string with magenta
|
||||||
|
// foreground.
|
||||||
|
func MagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyanString is a convenient helper function to return a string with cyan
|
||||||
|
// foreground.
|
||||||
|
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// WhiteString is a convenient helper function to return a string with white
|
||||||
|
// foreground.
|
||||||
|
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||||
|
|
||||||
|
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||||
|
|
||||||
|
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||||
|
|
||||||
|
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||||
|
|
||||||
|
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||||
|
// foreground.
|
||||||
|
func HiBlackString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiBlack, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||||
|
// foreground.
|
||||||
|
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||||
|
// foreground.
|
||||||
|
func HiGreenString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiGreen, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||||
|
// foreground.
|
||||||
|
func HiYellowString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiYellow, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||||
|
// foreground.
|
||||||
|
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||||
|
// foreground.
|
||||||
|
func HiMagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||||
|
// foreground.
|
||||||
|
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||||
|
// foreground.
|
||||||
|
func HiWhiteString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiWhite, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
|
||||||
|
func sprintln(a ...interface{}) string {
|
||||||
|
return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Opt-in for ansi color support for current process.
|
||||||
|
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
|
||||||
|
var outMode uint32
|
||||||
|
out := windows.Handle(os.Stdout.Fd())
|
||||||
|
if err := windows.GetConsoleMode(out, &outMode); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outMode |= windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
_ = windows.SetConsoleMode(out, outMode)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Package color is an ANSI color package to output colorized or SGR defined
|
||||||
|
output to the standard output. The API can be used in several way, pick one
|
||||||
|
that suits you.
|
||||||
|
|
||||||
|
Use simple and default helper functions with predefined foreground colors:
|
||||||
|
|
||||||
|
color.Cyan("Prints text in cyan.")
|
||||||
|
|
||||||
|
// a newline will be appended automatically
|
||||||
|
color.Blue("Prints %s in blue.", "text")
|
||||||
|
|
||||||
|
// More default foreground colors..
|
||||||
|
color.Red("We have red")
|
||||||
|
color.Yellow("Yellow color too!")
|
||||||
|
color.Magenta("And many others ..")
|
||||||
|
|
||||||
|
// Hi-intensity colors
|
||||||
|
color.HiGreen("Bright green color.")
|
||||||
|
color.HiBlack("Bright black means gray..")
|
||||||
|
color.HiWhite("Shiny white color!")
|
||||||
|
|
||||||
|
However, there are times when custom color mixes are required. Below are some
|
||||||
|
examples to create custom color objects and use the print functions of each
|
||||||
|
separate color object.
|
||||||
|
|
||||||
|
// Create a new color object
|
||||||
|
c := color.New(color.FgCyan).Add(color.Underline)
|
||||||
|
c.Println("Prints cyan text with an underline.")
|
||||||
|
|
||||||
|
// Or just add them to New()
|
||||||
|
d := color.New(color.FgCyan, color.Bold)
|
||||||
|
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||||
|
|
||||||
|
|
||||||
|
// Mix up foreground and background colors, create new mixes!
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
|
||||||
|
boldRed := red.Add(color.Bold)
|
||||||
|
boldRed.Println("This will print text in bold red.")
|
||||||
|
|
||||||
|
whiteBackground := red.Add(color.BgWhite)
|
||||||
|
whiteBackground.Println("Red text with White background.")
|
||||||
|
|
||||||
|
// Use your own io.Writer output
|
||||||
|
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||||
|
|
||||||
|
blue := color.New(color.FgBlue)
|
||||||
|
blue.Fprint(myWriter, "This will print text in blue.")
|
||||||
|
|
||||||
|
You can create PrintXxx functions to simplify even more:
|
||||||
|
|
||||||
|
// Create a custom print function for convenient
|
||||||
|
red := color.New(color.FgRed).PrintfFunc()
|
||||||
|
red("warning")
|
||||||
|
red("error: %s", err)
|
||||||
|
|
||||||
|
// Mix up multiple attributes
|
||||||
|
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||||
|
notice("don't forget this...")
|
||||||
|
|
||||||
|
You can also FprintXxx functions to pass your own io.Writer:
|
||||||
|
|
||||||
|
blue := color.New(FgBlue).FprintfFunc()
|
||||||
|
blue(myWriter, "important notice: %s", stars)
|
||||||
|
|
||||||
|
// Mix up with multiple attributes
|
||||||
|
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||||
|
success(myWriter, don't forget this...")
|
||||||
|
|
||||||
|
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||||
|
|
||||||
|
yellow := New(FgYellow).SprintFunc()
|
||||||
|
red := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Printf("this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Windows support is enabled by default. All Print functions work as intended.
|
||||||
|
However, only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||||
|
set the output to color.Output:
|
||||||
|
|
||||||
|
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
Using with existing code is possible. Just use the Set() method to set the
|
||||||
|
standard output to the given parameters. That way a rewrite of an existing
|
||||||
|
code is not required.
|
||||||
|
|
||||||
|
// Use handy standard colors.
|
||||||
|
color.Set(color.FgYellow)
|
||||||
|
|
||||||
|
fmt.Println("Existing text will be now in Yellow")
|
||||||
|
fmt.Printf("This one %s\n", "too")
|
||||||
|
|
||||||
|
color.Unset() // don't forget to unset
|
||||||
|
|
||||||
|
// You can mix up parameters
|
||||||
|
color.Set(color.FgMagenta, color.Bold)
|
||||||
|
defer color.Unset() // use it in your function
|
||||||
|
|
||||||
|
fmt.Println("All text will be now bold magenta.")
|
||||||
|
|
||||||
|
There might be a case where you want to disable color output (for example to
|
||||||
|
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||||
|
disable colors both globally and for single color definition. For example
|
||||||
|
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||||
|
the color output with:
|
||||||
|
|
||||||
|
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||||
|
|
||||||
|
if *flagNoColor {
|
||||||
|
color.NoColor = true // disables colorized output
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also disable the color by setting the NO_COLOR environment variable to any value.
|
||||||
|
|
||||||
|
It also has support for single color definitions (local). You can
|
||||||
|
disable/enable color output on the fly:
|
||||||
|
|
||||||
|
c := color.New(color.FgCyan)
|
||||||
|
c.Println("Prints cyan text")
|
||||||
|
|
||||||
|
c.DisableColor()
|
||||||
|
c.Println("This is printed without any color")
|
||||||
|
|
||||||
|
c.EnableColor()
|
||||||
|
c.Println("This prints again cyan...")
|
||||||
|
*/
|
||||||
|
package color
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.9
|
|
||||||
- "1.10"
|
|
||||||
- 1.11
|
|
||||||
- 1.12
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
# 1.7.0 (May 24, 2024)
|
||||||
|
|
||||||
|
ENHANCEMENTS:
|
||||||
|
|
||||||
|
- Remove `reflect` dependency ([#91](https://github.com/hashicorp/go-version/pull/91))
|
||||||
|
- Implement the `database/sql.Scanner` and `database/sql/driver.Value` interfaces for `Version` ([#133](https://github.com/hashicorp/go-version/pull/133))
|
||||||
|
|
||||||
|
INTERNAL:
|
||||||
|
|
||||||
|
- [COMPLIANCE] Add Copyright and License Headers ([#115](https://github.com/hashicorp/go-version/pull/115))
|
||||||
|
- [COMPLIANCE] Update MPL-2.0 LICENSE ([#105](https://github.com/hashicorp/go-version/pull/105))
|
||||||
|
- Bump actions/cache from 3.0.11 to 3.2.5 ([#116](https://github.com/hashicorp/go-version/pull/116))
|
||||||
|
- Bump actions/checkout from 3.2.0 to 3.3.0 ([#111](https://github.com/hashicorp/go-version/pull/111))
|
||||||
|
- Bump actions/upload-artifact from 3.1.1 to 3.1.2 ([#112](https://github.com/hashicorp/go-version/pull/112))
|
||||||
|
- GHA Migration ([#103](https://github.com/hashicorp/go-version/pull/103))
|
||||||
|
- github: Pin external GitHub Actions to hashes ([#107](https://github.com/hashicorp/go-version/pull/107))
|
||||||
|
- SEC-090: Automated trusted workflow pinning (2023-04-05) ([#124](https://github.com/hashicorp/go-version/pull/124))
|
||||||
|
- update readme ([#104](https://github.com/hashicorp/go-version/pull/104))
|
||||||
|
|
||||||
|
# 1.6.0 (June 28, 2022)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
- Add `Prerelease` function to `Constraint` to return true if the version includes a prerelease field ([#100](https://github.com/hashicorp/go-version/pull/100))
|
||||||
|
|
||||||
|
# 1.5.0 (May 18, 2022)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
- Use `encoding` `TextMarshaler` & `TextUnmarshaler` instead of JSON equivalents ([#95](https://github.com/hashicorp/go-version/pull/95))
|
||||||
|
- Add JSON handlers to allow parsing from/to JSON ([#93](https://github.com/hashicorp/go-version/pull/93))
|
||||||
|
|
||||||
|
# 1.4.0 (January 5, 2022)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
- Introduce `MustConstraints()` ([#87](https://github.com/hashicorp/go-version/pull/87))
|
||||||
|
- `Constraints`: Introduce `Equals()` and `sort.Interface` methods ([#88](https://github.com/hashicorp/go-version/pull/88))
|
||||||
|
|
||||||
|
# 1.3.0 (March 31, 2021)
|
||||||
|
|
||||||
|
Please note that CHANGELOG.md does not exist in the source code prior to this release.
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
- Add `Core` function to return a version without prerelease or metadata ([#85](https://github.com/hashicorp/go-version/pull/85))
|
||||||
|
|
||||||
|
# 1.2.1 (June 17, 2020)
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
- Prevent `Version.Equal` method from panicking on `nil` encounter ([#73](https://github.com/hashicorp/go-version/pull/73))
|
||||||
|
|
||||||
|
# 1.2.0 (April 23, 2019)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
- Add `GreaterThanOrEqual` and `LessThanOrEqual` helper methods ([#53](https://github.com/hashicorp/go-version/pull/53))
|
||||||
|
|
||||||
|
# 1.1.0 (Jan 07, 2019)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
- Add `NewSemver` constructor ([#45](https://github.com/hashicorp/go-version/pull/45))
|
||||||
|
|
||||||
|
# 1.0.0 (August 24, 2018)
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
Copyright (c) 2014 HashiCorp, Inc.
|
||||||
|
|
||||||
Mozilla Public License, version 2.0
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
1. Definitions
|
1. Definitions
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# Versioning Library for Go
|
# Versioning Library for Go
|
||||||
[](https://travis-ci.org/hashicorp/go-version)
|

|
||||||
|
[](https://godoc.org/github.com/hashicorp/go-version)
|
||||||
|
|
||||||
go-version is a library for parsing versions and version constraints,
|
go-version is a library for parsing versions and version constraints,
|
||||||
and verifying versions against a set of constraints. go-version
|
and verifying versions against a set of constraints. go-version
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -11,30 +14,40 @@ import (
|
||||||
// ">= 1.0".
|
// ">= 1.0".
|
||||||
type Constraint struct {
|
type Constraint struct {
|
||||||
f constraintFunc
|
f constraintFunc
|
||||||
|
op operator
|
||||||
check *Version
|
check *Version
|
||||||
original string
|
original string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Constraint) Equals(con *Constraint) bool {
|
||||||
|
return c.op == con.op && c.check.Equal(con.check)
|
||||||
|
}
|
||||||
|
|
||||||
// Constraints is a slice of constraints. We make a custom type so that
|
// Constraints is a slice of constraints. We make a custom type so that
|
||||||
// we can add methods to it.
|
// we can add methods to it.
|
||||||
type Constraints []*Constraint
|
type Constraints []*Constraint
|
||||||
|
|
||||||
type constraintFunc func(v, c *Version) bool
|
type constraintFunc func(v, c *Version) bool
|
||||||
|
|
||||||
var constraintOperators map[string]constraintFunc
|
var constraintOperators map[string]constraintOperation
|
||||||
|
|
||||||
|
type constraintOperation struct {
|
||||||
|
op operator
|
||||||
|
f constraintFunc
|
||||||
|
}
|
||||||
|
|
||||||
var constraintRegexp *regexp.Regexp
|
var constraintRegexp *regexp.Regexp
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
constraintOperators = map[string]constraintFunc{
|
constraintOperators = map[string]constraintOperation{
|
||||||
"": constraintEqual,
|
"": {op: equal, f: constraintEqual},
|
||||||
"=": constraintEqual,
|
"=": {op: equal, f: constraintEqual},
|
||||||
"!=": constraintNotEqual,
|
"!=": {op: notEqual, f: constraintNotEqual},
|
||||||
">": constraintGreaterThan,
|
">": {op: greaterThan, f: constraintGreaterThan},
|
||||||
"<": constraintLessThan,
|
"<": {op: lessThan, f: constraintLessThan},
|
||||||
">=": constraintGreaterThanEqual,
|
">=": {op: greaterThanEqual, f: constraintGreaterThanEqual},
|
||||||
"<=": constraintLessThanEqual,
|
"<=": {op: lessThanEqual, f: constraintLessThanEqual},
|
||||||
"~>": constraintPessimistic,
|
"~>": {op: pessimistic, f: constraintPessimistic},
|
||||||
}
|
}
|
||||||
|
|
||||||
ops := make([]string, 0, len(constraintOperators))
|
ops := make([]string, 0, len(constraintOperators))
|
||||||
|
|
@ -66,6 +79,16 @@ func NewConstraint(v string) (Constraints, error) {
|
||||||
return Constraints(result), nil
|
return Constraints(result), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustConstraints is a helper that wraps a call to a function
|
||||||
|
// returning (Constraints, error) and panics if error is non-nil.
|
||||||
|
func MustConstraints(c Constraints, err error) Constraints {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// Check tests if a version satisfies all the constraints.
|
// Check tests if a version satisfies all the constraints.
|
||||||
func (cs Constraints) Check(v *Version) bool {
|
func (cs Constraints) Check(v *Version) bool {
|
||||||
for _, c := range cs {
|
for _, c := range cs {
|
||||||
|
|
@ -77,6 +100,56 @@ func (cs Constraints) Check(v *Version) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equals compares Constraints with other Constraints
|
||||||
|
// for equality. This may not represent logical equivalence
|
||||||
|
// of compared constraints.
|
||||||
|
// e.g. even though '>0.1,>0.2' is logically equivalent
|
||||||
|
// to '>0.2' it is *NOT* treated as equal.
|
||||||
|
//
|
||||||
|
// Missing operator is treated as equal to '=', whitespaces
|
||||||
|
// are ignored and constraints are sorted before comaparison.
|
||||||
|
func (cs Constraints) Equals(c Constraints) bool {
|
||||||
|
if len(cs) != len(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// make copies to retain order of the original slices
|
||||||
|
left := make(Constraints, len(cs))
|
||||||
|
copy(left, cs)
|
||||||
|
sort.Stable(left)
|
||||||
|
right := make(Constraints, len(c))
|
||||||
|
copy(right, c)
|
||||||
|
sort.Stable(right)
|
||||||
|
|
||||||
|
// compare sorted slices
|
||||||
|
for i, con := range left {
|
||||||
|
if !con.Equals(right[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs Constraints) Len() int {
|
||||||
|
return len(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs Constraints) Less(i, j int) bool {
|
||||||
|
if cs[i].op < cs[j].op {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if cs[i].op > cs[j].op {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs[i].check.LessThan(cs[j].check)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs Constraints) Swap(i, j int) {
|
||||||
|
cs[i], cs[j] = cs[j], cs[i]
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the string format of the constraints
|
// Returns the string format of the constraints
|
||||||
func (cs Constraints) String() string {
|
func (cs Constraints) String() string {
|
||||||
csStr := make([]string, len(cs))
|
csStr := make([]string, len(cs))
|
||||||
|
|
@ -92,6 +165,12 @@ func (c *Constraint) Check(v *Version) bool {
|
||||||
return c.f(v, c.check)
|
return c.f(v, c.check)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prerelease returns true if the version underlying this constraint
|
||||||
|
// contains a prerelease field.
|
||||||
|
func (c *Constraint) Prerelease() bool {
|
||||||
|
return len(c.check.Prerelease()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Constraint) String() string {
|
func (c *Constraint) String() string {
|
||||||
return c.original
|
return c.original
|
||||||
}
|
}
|
||||||
|
|
@ -107,8 +186,11 @@ func parseSingle(v string) (*Constraint, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cop := constraintOperators[matches[1]]
|
||||||
|
|
||||||
return &Constraint{
|
return &Constraint{
|
||||||
f: constraintOperators[matches[1]],
|
f: cop.f,
|
||||||
|
op: cop.op,
|
||||||
check: check,
|
check: check,
|
||||||
original: v,
|
original: v,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -119,7 +201,7 @@ func prereleaseCheck(v, c *Version) bool {
|
||||||
case cPre && vPre:
|
case cPre && vPre:
|
||||||
// A constraint with a pre-release can only match a pre-release version
|
// A constraint with a pre-release can only match a pre-release version
|
||||||
// with the same base segments.
|
// with the same base segments.
|
||||||
return reflect.DeepEqual(c.Segments64(), v.Segments64())
|
return v.equalSegments(c)
|
||||||
|
|
||||||
case !cPre && vPre:
|
case !cPre && vPre:
|
||||||
// A constraint without a pre-release can only match a version without a
|
// A constraint without a pre-release can only match a version without a
|
||||||
|
|
@ -138,6 +220,18 @@ func prereleaseCheck(v, c *Version) bool {
|
||||||
// Constraint functions
|
// Constraint functions
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
|
|
||||||
|
type operator rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
equal operator = '='
|
||||||
|
notEqual operator = '≠'
|
||||||
|
greaterThan operator = '>'
|
||||||
|
lessThan operator = '<'
|
||||||
|
greaterThanEqual operator = '≥'
|
||||||
|
lessThanEqual operator = '≤'
|
||||||
|
pessimistic operator = '~'
|
||||||
|
)
|
||||||
|
|
||||||
func constraintEqual(v, c *Version) bool {
|
func constraintEqual(v, c *Version) bool {
|
||||||
return v.Equal(c)
|
return v.Equal(c)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -64,7 +67,6 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
|
||||||
}
|
}
|
||||||
segmentsStr := strings.Split(matches[1], ".")
|
segmentsStr := strings.Split(matches[1], ".")
|
||||||
segments := make([]int64, len(segmentsStr))
|
segments := make([]int64, len(segmentsStr))
|
||||||
si := 0
|
|
||||||
for i, str := range segmentsStr {
|
for i, str := range segmentsStr {
|
||||||
val, err := strconv.ParseInt(str, 10, 64)
|
val, err := strconv.ParseInt(str, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -72,8 +74,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
|
||||||
"Error parsing version: %s", err)
|
"Error parsing version: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
segments[i] = int64(val)
|
segments[i] = val
|
||||||
si++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even though we could support more than three segments, if we
|
// Even though we could support more than three segments, if we
|
||||||
|
|
@ -92,7 +93,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
|
||||||
metadata: matches[10],
|
metadata: matches[10],
|
||||||
pre: pre,
|
pre: pre,
|
||||||
segments: segments,
|
segments: segments,
|
||||||
si: si,
|
si: len(segmentsStr),
|
||||||
original: v,
|
original: v,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -119,11 +120,8 @@ func (v *Version) Compare(other *Version) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
segmentsSelf := v.Segments64()
|
|
||||||
segmentsOther := other.Segments64()
|
|
||||||
|
|
||||||
// If the segments are the same, we must compare on prerelease info
|
// If the segments are the same, we must compare on prerelease info
|
||||||
if reflect.DeepEqual(segmentsSelf, segmentsOther) {
|
if v.equalSegments(other) {
|
||||||
preSelf := v.Prerelease()
|
preSelf := v.Prerelease()
|
||||||
preOther := other.Prerelease()
|
preOther := other.Prerelease()
|
||||||
if preSelf == "" && preOther == "" {
|
if preSelf == "" && preOther == "" {
|
||||||
|
|
@ -139,6 +137,8 @@ func (v *Version) Compare(other *Version) int {
|
||||||
return comparePrereleases(preSelf, preOther)
|
return comparePrereleases(preSelf, preOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
segmentsSelf := v.Segments64()
|
||||||
|
segmentsOther := other.Segments64()
|
||||||
// Get the highest specificity (hS), or if they're equal, just use segmentSelf length
|
// Get the highest specificity (hS), or if they're equal, just use segmentSelf length
|
||||||
lenSelf := len(segmentsSelf)
|
lenSelf := len(segmentsSelf)
|
||||||
lenOther := len(segmentsOther)
|
lenOther := len(segmentsOther)
|
||||||
|
|
@ -162,7 +162,7 @@ func (v *Version) Compare(other *Version) int {
|
||||||
// this means Other had the lower specificity
|
// this means Other had the lower specificity
|
||||||
// Check to see if the remaining segments in Self are all zeros -
|
// Check to see if the remaining segments in Self are all zeros -
|
||||||
if !allZero(segmentsSelf[i:]) {
|
if !allZero(segmentsSelf[i:]) {
|
||||||
//if not, it means that Self has to be greater than Other
|
// if not, it means that Self has to be greater than Other
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
@ -182,6 +182,21 @@ func (v *Version) Compare(other *Version) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Version) equalSegments(other *Version) bool {
|
||||||
|
segmentsSelf := v.Segments64()
|
||||||
|
segmentsOther := other.Segments64()
|
||||||
|
|
||||||
|
if len(segmentsSelf) != len(segmentsOther) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range segmentsSelf {
|
||||||
|
if v != segmentsOther[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func allZero(segs []int64) bool {
|
func allZero(segs []int64) bool {
|
||||||
for _, s := range segs {
|
for _, s := range segs {
|
||||||
if s != 0 {
|
if s != 0 {
|
||||||
|
|
@ -278,8 +293,20 @@ func comparePrereleases(v string, other string) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Core returns a new version constructed from only the MAJOR.MINOR.PATCH
|
||||||
|
// segments of the version, without prerelease or metadata.
|
||||||
|
func (v *Version) Core() *Version {
|
||||||
|
segments := v.Segments64()
|
||||||
|
segmentsOnly := fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2])
|
||||||
|
return Must(NewVersion(segmentsOnly))
|
||||||
|
}
|
||||||
|
|
||||||
// Equal tests if two versions are equal.
|
// Equal tests if two versions are equal.
|
||||||
func (v *Version) Equal(o *Version) bool {
|
func (v *Version) Equal(o *Version) bool {
|
||||||
|
if v == nil || o == nil {
|
||||||
|
return v == o
|
||||||
|
}
|
||||||
|
|
||||||
return v.Compare(o) == 0
|
return v.Compare(o) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,7 +315,7 @@ func (v *Version) GreaterThan(o *Version) bool {
|
||||||
return v.Compare(o) > 0
|
return v.Compare(o) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GreaterThanOrEqualTo tests if this version is greater than or equal to another version.
|
// GreaterThanOrEqual tests if this version is greater than or equal to another version.
|
||||||
func (v *Version) GreaterThanOrEqual(o *Version) bool {
|
func (v *Version) GreaterThanOrEqual(o *Version) bool {
|
||||||
return v.Compare(o) >= 0
|
return v.Compare(o) >= 0
|
||||||
}
|
}
|
||||||
|
|
@ -298,7 +325,7 @@ func (v *Version) LessThan(o *Version) bool {
|
||||||
return v.Compare(o) < 0
|
return v.Compare(o) < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// LessThanOrEqualTo tests if this version is less than or equal to another version.
|
// LessThanOrEqual tests if this version is less than or equal to another version.
|
||||||
func (v *Version) LessThanOrEqual(o *Version) bool {
|
func (v *Version) LessThanOrEqual(o *Version) bool {
|
||||||
return v.Compare(o) <= 0
|
return v.Compare(o) <= 0
|
||||||
}
|
}
|
||||||
|
|
@ -378,3 +405,37 @@ func (v *Version) String() string {
|
||||||
func (v *Version) Original() string {
|
func (v *Version) Original() string {
|
||||||
return v.original
|
return v.original
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler interface.
|
||||||
|
func (v *Version) UnmarshalText(b []byte) error {
|
||||||
|
temp, err := NewVersion(string(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*v = *temp
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler interface.
|
||||||
|
func (v *Version) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(v.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
func (v *Version) Scan(src interface{}) error {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case string:
|
||||||
|
return v.UnmarshalText([]byte(src))
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot scan %T as Version", src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (v *Version) Value() (driver.Value, error) {
|
||||||
|
return v.String(), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
// Collection is a type that implements the sort.Interface interface
|
// Collection is a type that implements the sort.Interface interface
|
||||||
|
|
|
||||||
40
tests/tools/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
generated
vendored
40
tests/tools/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md
generated
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
# Windows Terminal Sequences
|
|
||||||
|
|
||||||
This library allow for enabling Windows terminal color support for Go.
|
|
||||||
|
|
||||||
See [Console Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) for details.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
sequences "github.com/konsorten/go-windows-terminal-sequences"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
sequences.EnableVirtualTerminalProcessing(syscall.Stdout, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Authors
|
|
||||||
|
|
||||||
The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
|
|
||||||
|
|
||||||
We thank all the authors who provided code to this library:
|
|
||||||
|
|
||||||
* Felix Kollmann
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2018 marvin + konsorten GmbH (open-source@konsorten.de)
|
|
||||||
|
|
||||||
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.
|
|
||||||
36
tests/tools/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
generated
vendored
36
tests/tools/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences.go
generated
vendored
|
|
@ -1,36 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package sequences
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kernel32Dll *syscall.LazyDLL = syscall.NewLazyDLL("Kernel32.dll")
|
|
||||||
setConsoleMode *syscall.LazyProc = kernel32Dll.NewProc("SetConsoleMode")
|
|
||||||
)
|
|
||||||
|
|
||||||
func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error {
|
|
||||||
const ENABLE_VIRTUAL_TERMINAL_PROCESSING uint32 = 0x4
|
|
||||||
|
|
||||||
var mode uint32
|
|
||||||
err := syscall.GetConsoleMode(syscall.Stdout, &mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if enable {
|
|
||||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
} else {
|
|
||||||
mode &^= ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
}
|
|
||||||
|
|
||||||
ret, _, err := setConsoleMode.Call(uintptr(unsafe.Pointer(stream)), uintptr(mode))
|
|
||||||
if ret == 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor 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, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2017 the Mage 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.
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package mg
|
||||||
|
|
||||||
|
// Color is ANSI color type
|
||||||
|
type Color int
|
||||||
|
|
||||||
|
// If you add/change/remove any items in this constant,
|
||||||
|
// you will need to run "stringer -type=Color" in this directory again.
|
||||||
|
// NOTE: Please keep the list in an alphabetical order.
|
||||||
|
const (
|
||||||
|
Black Color = iota
|
||||||
|
Red
|
||||||
|
Green
|
||||||
|
Yellow
|
||||||
|
Blue
|
||||||
|
Magenta
|
||||||
|
Cyan
|
||||||
|
White
|
||||||
|
BrightBlack
|
||||||
|
BrightRed
|
||||||
|
BrightGreen
|
||||||
|
BrightYellow
|
||||||
|
BrightBlue
|
||||||
|
BrightMagenta
|
||||||
|
BrightCyan
|
||||||
|
BrightWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnsiColor are ANSI color codes for supported terminal colors.
|
||||||
|
var ansiColor = map[Color]string{
|
||||||
|
Black: "\u001b[30m",
|
||||||
|
Red: "\u001b[31m",
|
||||||
|
Green: "\u001b[32m",
|
||||||
|
Yellow: "\u001b[33m",
|
||||||
|
Blue: "\u001b[34m",
|
||||||
|
Magenta: "\u001b[35m",
|
||||||
|
Cyan: "\u001b[36m",
|
||||||
|
White: "\u001b[37m",
|
||||||
|
BrightBlack: "\u001b[30;1m",
|
||||||
|
BrightRed: "\u001b[31;1m",
|
||||||
|
BrightGreen: "\u001b[32;1m",
|
||||||
|
BrightYellow: "\u001b[33;1m",
|
||||||
|
BrightBlue: "\u001b[34;1m",
|
||||||
|
BrightMagenta: "\u001b[35;1m",
|
||||||
|
BrightCyan: "\u001b[36;1m",
|
||||||
|
BrightWhite: "\u001b[37;1m",
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnsiColorReset is an ANSI color code to reset the terminal color.
|
||||||
|
const AnsiColorReset = "\033[0m"
|
||||||
|
|
||||||
|
// DefaultTargetAnsiColor is a default ANSI color for colorizing targets.
|
||||||
|
// It is set to Cyan as an arbitrary color, because it has a neutral meaning
|
||||||
|
var DefaultTargetAnsiColor = ansiColor[Cyan]
|
||||||
|
|
||||||
|
func toLowerCase(s string) string {
|
||||||
|
// this is a naive implementation
|
||||||
|
// borrowed from https://golang.org/src/strings/strings.go
|
||||||
|
// and only considers alphabetical characters [a-zA-Z]
|
||||||
|
// so that we don't depend on the "strings" package
|
||||||
|
buf := make([]byte, len(s))
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
if 'A' <= c && c <= 'Z' {
|
||||||
|
c += 'a' - 'A'
|
||||||
|
}
|
||||||
|
buf[i] = c
|
||||||
|
}
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAnsiColor(color string) (string, bool) {
|
||||||
|
colorLower := toLowerCase(color)
|
||||||
|
for k, v := range ansiColor {
|
||||||
|
colorConstLower := toLowerCase(k.String())
|
||||||
|
if colorConstLower == colorLower {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Code generated by "stringer -type=Color"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package mg
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Black-0]
|
||||||
|
_ = x[Red-1]
|
||||||
|
_ = x[Green-2]
|
||||||
|
_ = x[Yellow-3]
|
||||||
|
_ = x[Blue-4]
|
||||||
|
_ = x[Magenta-5]
|
||||||
|
_ = x[Cyan-6]
|
||||||
|
_ = x[White-7]
|
||||||
|
_ = x[BrightBlack-8]
|
||||||
|
_ = x[BrightRed-9]
|
||||||
|
_ = x[BrightGreen-10]
|
||||||
|
_ = x[BrightYellow-11]
|
||||||
|
_ = x[BrightBlue-12]
|
||||||
|
_ = x[BrightMagenta-13]
|
||||||
|
_ = x[BrightCyan-14]
|
||||||
|
_ = x[BrightWhite-15]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Color_name = "BlackRedGreenYellowBlueMagentaCyanWhiteBrightBlackBrightRedBrightGreenBrightYellowBrightBlueBrightMagentaBrightCyanBrightWhite"
|
||||||
|
|
||||||
|
var _Color_index = [...]uint8{0, 5, 8, 13, 19, 23, 30, 34, 39, 50, 59, 70, 82, 92, 105, 115, 126}
|
||||||
|
|
||||||
|
func (i Color) String() string {
|
||||||
|
if i < 0 || i >= Color(len(_Color_index)-1) {
|
||||||
|
return "Color(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Color_name[_Color_index[i]:_Color_index[i+1]]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
package mg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = log.New(os.Stderr, "", 0)
|
||||||
|
|
||||||
|
type onceMap struct {
|
||||||
|
mu *sync.Mutex
|
||||||
|
m map[onceKey]*onceFun
|
||||||
|
}
|
||||||
|
|
||||||
|
type onceKey struct {
|
||||||
|
Name string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *onceMap) LoadOrStore(f Fn) *onceFun {
|
||||||
|
defer o.mu.Unlock()
|
||||||
|
o.mu.Lock()
|
||||||
|
|
||||||
|
key := onceKey{
|
||||||
|
Name: f.Name(),
|
||||||
|
ID: f.ID(),
|
||||||
|
}
|
||||||
|
existing, ok := o.m[key]
|
||||||
|
if ok {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
one := &onceFun{
|
||||||
|
once: &sync.Once{},
|
||||||
|
fn: f,
|
||||||
|
displayName: displayName(f.Name()),
|
||||||
|
}
|
||||||
|
o.m[key] = one
|
||||||
|
return one
|
||||||
|
}
|
||||||
|
|
||||||
|
var onces = &onceMap{
|
||||||
|
mu: &sync.Mutex{},
|
||||||
|
m: map[onceKey]*onceFun{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerialDeps is like Deps except it runs each dependency serially, instead of
|
||||||
|
// in parallel. This can be useful for resource intensive dependencies that
|
||||||
|
// shouldn't be run at the same time.
|
||||||
|
func SerialDeps(fns ...interface{}) {
|
||||||
|
funcs := checkFns(fns)
|
||||||
|
ctx := context.Background()
|
||||||
|
for i := range fns {
|
||||||
|
runDeps(ctx, funcs[i:i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerialCtxDeps is like CtxDeps except it runs each dependency serially,
|
||||||
|
// instead of in parallel. This can be useful for resource intensive
|
||||||
|
// dependencies that shouldn't be run at the same time.
|
||||||
|
func SerialCtxDeps(ctx context.Context, fns ...interface{}) {
|
||||||
|
funcs := checkFns(fns)
|
||||||
|
for i := range fns {
|
||||||
|
runDeps(ctx, funcs[i:i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CtxDeps runs the given functions as dependencies of the calling function.
|
||||||
|
// Dependencies must only be of type:
|
||||||
|
// func()
|
||||||
|
// func() error
|
||||||
|
// func(context.Context)
|
||||||
|
// func(context.Context) error
|
||||||
|
// Or a similar method on a mg.Namespace type.
|
||||||
|
// Or an mg.Fn interface.
|
||||||
|
//
|
||||||
|
// The function calling Deps is guaranteed that all dependent functions will be
|
||||||
|
// run exactly once when Deps returns. Dependent functions may in turn declare
|
||||||
|
// their own dependencies using Deps. Each dependency is run in their own
|
||||||
|
// goroutines. Each function is given the context provided if the function
|
||||||
|
// prototype allows for it.
|
||||||
|
func CtxDeps(ctx context.Context, fns ...interface{}) {
|
||||||
|
funcs := checkFns(fns)
|
||||||
|
runDeps(ctx, funcs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runDeps assumes you've already called checkFns.
|
||||||
|
func runDeps(ctx context.Context, fns []Fn) {
|
||||||
|
mu := &sync.Mutex{}
|
||||||
|
var errs []string
|
||||||
|
var exit int
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
for _, f := range fns {
|
||||||
|
fn := onces.LoadOrStore(f)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if v := recover(); v != nil {
|
||||||
|
mu.Lock()
|
||||||
|
if err, ok := v.(error); ok {
|
||||||
|
exit = changeExit(exit, ExitStatus(err))
|
||||||
|
} else {
|
||||||
|
exit = changeExit(exit, 1)
|
||||||
|
}
|
||||||
|
errs = append(errs, fmt.Sprint(v))
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
if err := fn.run(ctx); err != nil {
|
||||||
|
mu.Lock()
|
||||||
|
errs = append(errs, fmt.Sprint(err))
|
||||||
|
exit = changeExit(exit, ExitStatus(err))
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
panic(Fatal(exit, strings.Join(errs, "\n")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFns(fns []interface{}) []Fn {
|
||||||
|
funcs := make([]Fn, len(fns))
|
||||||
|
for i, f := range fns {
|
||||||
|
if fn, ok := f.(Fn); ok {
|
||||||
|
funcs[i] = fn
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the target provided is a not function so we can give a clear warning
|
||||||
|
t := reflect.TypeOf(f)
|
||||||
|
if t == nil || t.Kind() != reflect.Func {
|
||||||
|
panic(fmt.Errorf("non-function used as a target dependency: %T. The mg.Deps, mg.SerialDeps and mg.CtxDeps functions accept function names, such as mg.Deps(TargetA, TargetB)", f))
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs[i] = F(f)
|
||||||
|
}
|
||||||
|
return funcs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deps runs the given functions in parallel, exactly once. Dependencies must
|
||||||
|
// only be of type:
|
||||||
|
// func()
|
||||||
|
// func() error
|
||||||
|
// func(context.Context)
|
||||||
|
// func(context.Context) error
|
||||||
|
// Or a similar method on a mg.Namespace type.
|
||||||
|
// Or an mg.Fn interface.
|
||||||
|
//
|
||||||
|
// This is a way to build up a tree of dependencies with each dependency
|
||||||
|
// defining its own dependencies. Functions must have the same signature as a
|
||||||
|
// Mage target, i.e. optional context argument, optional error return.
|
||||||
|
func Deps(fns ...interface{}) {
|
||||||
|
CtxDeps(context.Background(), fns...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeExit(old, new int) int {
|
||||||
|
if new == 0 {
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
if old == 0 {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
if old == new {
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
// both different and both non-zero, just set
|
||||||
|
// exit to 1. Nothing more we can do.
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcName returns the unique name for the function
|
||||||
|
func funcName(i interface{}) string {
|
||||||
|
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayName(name string) string {
|
||||||
|
splitByPackage := strings.Split(name, ".")
|
||||||
|
if len(splitByPackage) == 2 && splitByPackage[0] == "main" {
|
||||||
|
return splitByPackage[len(splitByPackage)-1]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
type onceFun struct {
|
||||||
|
once *sync.Once
|
||||||
|
fn Fn
|
||||||
|
err error
|
||||||
|
|
||||||
|
displayName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// run will run the function exactly once and capture the error output. Further runs simply return
|
||||||
|
// the same error output.
|
||||||
|
func (o *onceFun) run(ctx context.Context) error {
|
||||||
|
o.once.Do(func() {
|
||||||
|
if Verbose() {
|
||||||
|
logger.Println("Running dependency:", displayName(o.fn.Name()))
|
||||||
|
}
|
||||||
|
o.err = o.fn.Run(ctx)
|
||||||
|
})
|
||||||
|
return o.err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package mg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fatalErr struct {
|
||||||
|
code int
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fatalErr) ExitStatus() int {
|
||||||
|
return f.code
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitStatus interface {
|
||||||
|
ExitStatus() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal returns an error that will cause mage to print out the
|
||||||
|
// given args and exit with the given exit code.
|
||||||
|
func Fatal(code int, args ...interface{}) error {
|
||||||
|
return fatalErr{
|
||||||
|
code: code,
|
||||||
|
error: errors.New(fmt.Sprint(args...)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf returns an error that will cause mage to print out the
|
||||||
|
// given message and exit with the given exit code.
|
||||||
|
func Fatalf(code int, format string, args ...interface{}) error {
|
||||||
|
return fatalErr{
|
||||||
|
code: code,
|
||||||
|
error: fmt.Errorf(format, args...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExitStatus queries the error for an exit status. If the error is nil, it
|
||||||
|
// returns 0. If the error does not implement ExitStatus() int, it returns 1.
|
||||||
|
// Otherwise it retiurns the value from ExitStatus().
|
||||||
|
func ExitStatus(err error) int {
|
||||||
|
if err == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
exit, ok := err.(exitStatus)
|
||||||
|
if !ok {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return exit.ExitStatus()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
package mg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fn represents a function that can be run with mg.Deps. Package, Name, and ID must combine to
|
||||||
|
// uniquely identify a function, while ensuring the "same" function has identical values. These are
|
||||||
|
// used as a map key to find and run (or not run) the function.
|
||||||
|
type Fn interface {
|
||||||
|
// Name should return the fully qualified name of the function. Usually
|
||||||
|
// it's best to use runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name().
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// ID should be an additional uniqueness qualifier in case the name is insufficiently unique.
|
||||||
|
// This can be the case for functions that take arguments (mg.F json-encodes an array of the
|
||||||
|
// args).
|
||||||
|
ID() string
|
||||||
|
|
||||||
|
// Run should run the function.
|
||||||
|
Run(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// F takes a function that is compatible as a mage target, and any args that need to be passed to
|
||||||
|
// it, and wraps it in an mg.Fn that mg.Deps can run. Args must be passed in the same order as they
|
||||||
|
// are declared by the function. Note that you do not need to and should not pass a context.Context
|
||||||
|
// to F, even if the target takes a context. Compatible args are int, bool, string, and
|
||||||
|
// time.Duration.
|
||||||
|
func F(target interface{}, args ...interface{}) Fn {
|
||||||
|
hasContext, isNamespace, err := checkF(target, args)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
id, err := json.Marshal(args)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can't convert args into a mage-compatible id for mg.Deps: %s", err))
|
||||||
|
}
|
||||||
|
return fn{
|
||||||
|
name: funcName(target),
|
||||||
|
id: string(id),
|
||||||
|
f: func(ctx context.Context) error {
|
||||||
|
v := reflect.ValueOf(target)
|
||||||
|
count := len(args)
|
||||||
|
if hasContext {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if isNamespace {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
vargs := make([]reflect.Value, count)
|
||||||
|
x := 0
|
||||||
|
if isNamespace {
|
||||||
|
vargs[0] = reflect.ValueOf(struct{}{})
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
if hasContext {
|
||||||
|
vargs[x] = reflect.ValueOf(ctx)
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
for y := range args {
|
||||||
|
vargs[x+y] = reflect.ValueOf(args[y])
|
||||||
|
}
|
||||||
|
ret := v.Call(vargs)
|
||||||
|
if len(ret) > 0 {
|
||||||
|
// we only allow functions with a single error return, so this should be safe.
|
||||||
|
if ret[0].IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ret[0].Interface().(error)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fn struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
f func(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the fully qualified name of the function.
|
||||||
|
func (f fn) Name() string {
|
||||||
|
return f.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns a hash of the argument values passed in
|
||||||
|
func (f fn) ID() string {
|
||||||
|
return f.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs the function.
|
||||||
|
func (f fn) Run(ctx context.Context) error {
|
||||||
|
return f.f(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkF(target interface{}, args []interface{}) (hasContext, isNamespace bool, _ error) {
|
||||||
|
t := reflect.TypeOf(target)
|
||||||
|
if t == nil || t.Kind() != reflect.Func {
|
||||||
|
return false, false, fmt.Errorf("non-function passed to mg.F: %T. The mg.F function accepts function names, such as mg.F(TargetA, \"arg1\", \"arg2\")", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumOut() > 1 {
|
||||||
|
return false, false, fmt.Errorf("target has too many return values, must be zero or just an error: %T", target)
|
||||||
|
}
|
||||||
|
if t.NumOut() == 1 && t.Out(0) != errType {
|
||||||
|
return false, false, fmt.Errorf("target's return value is not an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// more inputs than slots is an error if not variadic
|
||||||
|
if len(args) > t.NumIn() && !t.IsVariadic() {
|
||||||
|
return false, false, fmt.Errorf("too many arguments for target, got %d for %T", len(args), target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumIn() == 0 {
|
||||||
|
return false, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
x := 0
|
||||||
|
inputs := t.NumIn()
|
||||||
|
|
||||||
|
if t.In(0).AssignableTo(emptyType) {
|
||||||
|
// nameSpace func
|
||||||
|
isNamespace = true
|
||||||
|
x++
|
||||||
|
// callers must leave off the namespace value
|
||||||
|
inputs--
|
||||||
|
}
|
||||||
|
if t.NumIn() > x && t.In(x) == ctxType {
|
||||||
|
// callers must leave off the context
|
||||||
|
inputs--
|
||||||
|
|
||||||
|
// let the upper function know it should pass us a context.
|
||||||
|
hasContext = true
|
||||||
|
|
||||||
|
// skip checking the first argument in the below loop if it's a context, since first arg is
|
||||||
|
// special.
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.IsVariadic() {
|
||||||
|
if len(args) < inputs-1 {
|
||||||
|
return false, false, fmt.Errorf("too few arguments for target, got %d for %T", len(args), target)
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if len(args) != inputs {
|
||||||
|
return false, false, fmt.Errorf("wrong number of arguments for target, got %d for %T", len(args), target)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
argT := t.In(x)
|
||||||
|
if t.IsVariadic() && x == t.NumIn()-1 {
|
||||||
|
// For the variadic argument, use the slice element type.
|
||||||
|
argT = argT.Elem()
|
||||||
|
}
|
||||||
|
if !argTypes[argT] {
|
||||||
|
return false, false, fmt.Errorf("argument %d (%s), is not a supported argument type", x, argT)
|
||||||
|
}
|
||||||
|
passedT := reflect.TypeOf(arg)
|
||||||
|
if argT != passedT {
|
||||||
|
return false, false, fmt.Errorf("argument %d expected to be %s, but is %s", x, argT, passedT)
|
||||||
|
}
|
||||||
|
if x < t.NumIn()-1 {
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasContext, isNamespace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we define the types that are supported as arguments/returns
|
||||||
|
var (
|
||||||
|
ctxType = reflect.TypeOf(func(context.Context) {}).In(0)
|
||||||
|
errType = reflect.TypeOf(func() error { return nil }).Out(0)
|
||||||
|
emptyType = reflect.TypeOf(struct{}{})
|
||||||
|
|
||||||
|
intType = reflect.TypeOf(int(0))
|
||||||
|
stringType = reflect.TypeOf(string(""))
|
||||||
|
boolType = reflect.TypeOf(bool(false))
|
||||||
|
durType = reflect.TypeOf(time.Second)
|
||||||
|
|
||||||
|
// don't put ctx in here, this is for non-context types
|
||||||
|
argTypes = map[reflect.Type]bool{
|
||||||
|
intType: true,
|
||||||
|
boolType: true,
|
||||||
|
stringType: true,
|
||||||
|
durType: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
package mg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CacheEnv is the environment variable that users may set to change the
|
||||||
|
// location where mage stores its compiled binaries.
|
||||||
|
const CacheEnv = "MAGEFILE_CACHE"
|
||||||
|
|
||||||
|
// VerboseEnv is the environment variable that indicates the user requested
|
||||||
|
// verbose mode when running a magefile.
|
||||||
|
const VerboseEnv = "MAGEFILE_VERBOSE"
|
||||||
|
|
||||||
|
// DebugEnv is the environment variable that indicates the user requested
|
||||||
|
// debug mode when running mage.
|
||||||
|
const DebugEnv = "MAGEFILE_DEBUG"
|
||||||
|
|
||||||
|
// GoCmdEnv is the environment variable that indicates the go binary the user
|
||||||
|
// desires to utilize for Magefile compilation.
|
||||||
|
const GoCmdEnv = "MAGEFILE_GOCMD"
|
||||||
|
|
||||||
|
// IgnoreDefaultEnv is the environment variable that indicates the user requested
|
||||||
|
// to ignore the default target specified in the magefile.
|
||||||
|
const IgnoreDefaultEnv = "MAGEFILE_IGNOREDEFAULT"
|
||||||
|
|
||||||
|
// HashFastEnv is the environment variable that indicates the user requested to
|
||||||
|
// use a quick hash of magefiles to determine whether or not the magefile binary
|
||||||
|
// needs to be rebuilt. This results in faster runtimes, but means that mage
|
||||||
|
// will fail to rebuild if a dependency has changed. To force a rebuild, run
|
||||||
|
// mage with the -f flag.
|
||||||
|
const HashFastEnv = "MAGEFILE_HASHFAST"
|
||||||
|
|
||||||
|
// EnableColorEnv is the environment variable that indicates the user is using
|
||||||
|
// a terminal which supports a color output. The default is false for backwards
|
||||||
|
// compatibility. When the value is true and the detected terminal does support colors
|
||||||
|
// then the list of mage targets will be displayed in ANSI color. When the value
|
||||||
|
// is true but the detected terminal does not support colors, then the list of
|
||||||
|
// mage targets will be displayed in the default colors (e.g. black and white).
|
||||||
|
const EnableColorEnv = "MAGEFILE_ENABLE_COLOR"
|
||||||
|
|
||||||
|
// TargetColorEnv is the environment variable that indicates which ANSI color
|
||||||
|
// should be used to colorize mage targets. This is only applicable when
|
||||||
|
// the MAGEFILE_ENABLE_COLOR environment variable is true.
|
||||||
|
// The supported ANSI color names are any of these:
|
||||||
|
// - Black
|
||||||
|
// - Red
|
||||||
|
// - Green
|
||||||
|
// - Yellow
|
||||||
|
// - Blue
|
||||||
|
// - Magenta
|
||||||
|
// - Cyan
|
||||||
|
// - White
|
||||||
|
// - BrightBlack
|
||||||
|
// - BrightRed
|
||||||
|
// - BrightGreen
|
||||||
|
// - BrightYellow
|
||||||
|
// - BrightBlue
|
||||||
|
// - BrightMagenta
|
||||||
|
// - BrightCyan
|
||||||
|
// - BrightWhite
|
||||||
|
const TargetColorEnv = "MAGEFILE_TARGET_COLOR"
|
||||||
|
|
||||||
|
// Verbose reports whether a magefile was run with the verbose flag.
|
||||||
|
func Verbose() bool {
|
||||||
|
b, _ := strconv.ParseBool(os.Getenv(VerboseEnv))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug reports whether a magefile was run with the debug flag.
|
||||||
|
func Debug() bool {
|
||||||
|
b, _ := strconv.ParseBool(os.Getenv(DebugEnv))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoCmd reports the command that Mage will use to build go code. By default mage runs
|
||||||
|
// the "go" binary in the PATH.
|
||||||
|
func GoCmd() string {
|
||||||
|
if cmd := os.Getenv(GoCmdEnv); cmd != "" {
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
return "go"
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFast reports whether the user has requested to use the fast hashing
|
||||||
|
// mechanism rather than rely on go's rebuilding mechanism.
|
||||||
|
func HashFast() bool {
|
||||||
|
b, _ := strconv.ParseBool(os.Getenv(HashFastEnv))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// IgnoreDefault reports whether the user has requested to ignore the default target
|
||||||
|
// in the magefile.
|
||||||
|
func IgnoreDefault() bool {
|
||||||
|
b, _ := strconv.ParseBool(os.Getenv(IgnoreDefaultEnv))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheDir returns the directory where mage caches compiled binaries. It
|
||||||
|
// defaults to $HOME/.magefile, but may be overridden by the MAGEFILE_CACHE
|
||||||
|
// environment variable.
|
||||||
|
func CacheDir() string {
|
||||||
|
d := os.Getenv(CacheEnv)
|
||||||
|
if d != "" {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
return filepath.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"), "magefile")
|
||||||
|
default:
|
||||||
|
return filepath.Join(os.Getenv("HOME"), ".magefile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColor reports whether the user has requested to enable a color output.
|
||||||
|
func EnableColor() bool {
|
||||||
|
b, _ := strconv.ParseBool(os.Getenv(EnableColorEnv))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetColor returns the configured ANSI color name a color output.
|
||||||
|
func TargetColor() string {
|
||||||
|
s, exists := os.LookupEnv(TargetColorEnv)
|
||||||
|
if exists {
|
||||||
|
if c, ok := getAnsiColor(s); ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DefaultTargetAnsiColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespace allows for the grouping of similar commands
|
||||||
|
type Namespace struct{}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# go-colorable
|
||||||
|
|
||||||
|
[](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest)
|
||||||
|
[](https://codecov.io/gh/mattn/go-colorable)
|
||||||
|
[](http://godoc.org/github.com/mattn/go-colorable)
|
||||||
|
[](https://goreportcard.com/report/mattn/go-colorable)
|
||||||
|
|
||||||
|
Colorable writer for windows.
|
||||||
|
|
||||||
|
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||||
|
This package is possible to handle escape sequence for ansi color on windows.
|
||||||
|
|
||||||
|
## Too Bad!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## So Good!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||||
|
logrus.SetOutput(colorable.NewColorableStdout())
|
||||||
|
|
||||||
|
logrus.Info("succeeded")
|
||||||
|
logrus.Warn("not correct")
|
||||||
|
logrus.Error("something error")
|
||||||
|
logrus.Fatal("panic")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can compile above code on non-windows OSs.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/mattn/go-colorable
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
# Author
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
38
tests/tools/vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
38
tests/tools/vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
//go:build !windows || appengine
|
||||||
|
// +build !windows appengine
|
||||||
|
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewColorable returns new instance of Writer which handles escape sequence.
|
||||||
|
func NewColorable(file *os.File) io.Writer {
|
||||||
|
if file == nil {
|
||||||
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
||||||
|
func NewColorableStdout() io.Writer {
|
||||||
|
return os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
||||||
|
func NewColorableStderr() io.Writer {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableColorsStdout enable colors if possible.
|
||||||
|
func EnableColorsStdout(enabled *bool) func() {
|
||||||
|
if enabled != nil {
|
||||||
|
*enabled = true
|
||||||
|
}
|
||||||
|
return func() {}
|
||||||
|
}
|
||||||
1047
tests/tools/vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
1047
tests/tools/vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
echo "" > coverage.txt
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -v vendor); do
|
||||||
|
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out >> coverage.txt
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonColorable holds writer but removes escape sequence.
|
||||||
|
type NonColorable struct {
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
|
||||||
|
func NewNonColorable(w io.Writer) io.Writer {
|
||||||
|
return &NonColorable{out: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data on console
|
||||||
|
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||||
|
er := bytes.NewReader(data)
|
||||||
|
var plaintext bytes.Buffer
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
c1, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
plaintext.WriteTo(w.out)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c1 != 0x1b {
|
||||||
|
plaintext.WriteByte(c1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err = plaintext.WriteTo(w.out)
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
c2, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c2 != 0x5b {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
(The MIT License)
|
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
|
||||||
|
|
||||||
Copyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de)
|
MIT License (Expat)
|
||||||
|
|
||||||
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:
|
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 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.
|
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.
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
# go-isatty
|
||||||
|
|
||||||
|
[](http://godoc.org/github.com/mattn/go-isatty)
|
||||||
|
[](https://codecov.io/gh/mattn/go-isatty)
|
||||||
|
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
|
||||||
|
[](https://goreportcard.com/report/mattn/go-isatty)
|
||||||
|
|
||||||
|
isatty for golang
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
|
fmt.Println("Is Terminal")
|
||||||
|
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||||
|
fmt.Println("Is Cygwin/MSYS2 Terminal")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Is Not Terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/mattn/go-isatty
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Yasuhiro Matsumoto (a.k.a mattn)
|
||||||
|
|
||||||
|
## Thanks
|
||||||
|
|
||||||
|
* k-takata: base idea for IsCygwinTerminal
|
||||||
|
|
||||||
|
https://github.com/k-takata/go-iscygpty
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Package isatty implements interface to isatty
|
||||||
|
package isatty
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
echo "" > coverage.txt
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -v vendor); do
|
||||||
|
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out >> coverage.txt
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine && !tinygo
|
||||||
|
// +build darwin freebsd openbsd netbsd dragonfly hurd
|
||||||
|
// +build !appengine
|
||||||
|
// +build !tinygo
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
//go:build (appengine || js || nacl || tinygo || wasm) && !windows
|
||||||
|
// +build appengine js nacl tinygo wasm
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
// IsTerminal returns true if the file descriptor is terminal which
|
||||||
|
// is always false on js and appengine classic which is a sandboxed PaaS.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
//go:build plan9
|
||||||
|
// +build plan9
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
path, err := syscall.Fd2path(int(fd))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return path == "/dev/cons" || path == "/mnt/term/dev/cons"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//go:build solaris && !appengine
|
||||||
|
// +build solaris,!appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
_, err := unix.IoctlGetTermio(int(fd), unix.TCGETA)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build (linux || aix || zos) && !appengine && !tinygo
|
||||||
|
// +build linux aix zos
|
||||||
|
// +build !appengine
|
||||||
|
// +build !tinygo
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal. This is also always false on this environment.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
//go:build windows && !appengine
|
||||||
|
// +build windows,!appengine
|
||||||
|
|
||||||
|
package isatty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
objectNameInfo uintptr = 1
|
||||||
|
fileNameInfo = 2
|
||||||
|
fileTypePipe = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
ntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||||
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||||
|
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procGetFileType = kernel32.NewProc("GetFileType")
|
||||||
|
procNtQueryObject = ntdll.NewProc("NtQueryObject")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Check if GetFileInformationByHandleEx is available.
|
||||||
|
if procGetFileInformationByHandleEx.Find() != nil {
|
||||||
|
procGetFileInformationByHandleEx = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal return true if the file descriptor is terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var st uint32
|
||||||
|
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||||
|
return r != 0 && e == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check pipe name is used for cygwin/msys2 pty.
|
||||||
|
// Cygwin/MSYS2 PTY has a name like:
|
||||||
|
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||||
|
func isCygwinPipeName(name string) bool {
|
||||||
|
token := strings.Split(name, "-")
|
||||||
|
if len(token) < 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[0] != `\msys` &&
|
||||||
|
token[0] != `\cygwin` &&
|
||||||
|
token[0] != `\Device\NamedPipe\msys` &&
|
||||||
|
token[0] != `\Device\NamedPipe\cygwin` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[1] == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(token[2], "pty") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[3] != `from` && token[3] != `to` {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if token[4] != "master" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
||||||
|
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
|
||||||
|
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
||||||
|
// Windows vista to 10
|
||||||
|
// see https://stackoverflow.com/a/18792477 for details
|
||||||
|
func getFileNameByHandle(fd uintptr) (string, error) {
|
||||||
|
if procNtQueryObject == nil {
|
||||||
|
return "", errors.New("ntdll.dll: NtQueryObject not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [4 + syscall.MAX_PATH]uint16
|
||||||
|
var result int
|
||||||
|
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
|
||||||
|
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
|
||||||
|
if r != 0 {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||||
|
// terminal.
|
||||||
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
|
if procGetFileInformationByHandleEx == nil {
|
||||||
|
name, err := getFileNameByHandle(fd)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isCygwinPipeName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cygwin/msys's pty is a pipe.
|
||||||
|
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
||||||
|
if ft != fileTypePipe || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [2 + syscall.MAX_PATH]uint16
|
||||||
|
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
||||||
|
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
||||||
|
uintptr(len(buf)*2), 0, 0)
|
||||||
|
if r == 0 || e != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
l := *(*uint32)(unsafe.Pointer(&buf))
|
||||||
|
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
logrus
|
logrus
|
||||||
vendor
|
vendor
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
run:
|
||||||
|
# do not run on test files yet
|
||||||
|
tests: false
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
linters-settings:
|
||||||
|
errcheck:
|
||||||
|
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||||
|
# default is false: such cases aren't reported by default.
|
||||||
|
check-type-assertions: false
|
||||||
|
|
||||||
|
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||||
|
# default is false: such cases aren't reported by default.
|
||||||
|
check-blank: false
|
||||||
|
|
||||||
|
lll:
|
||||||
|
line-length: 100
|
||||||
|
tab-width: 4
|
||||||
|
|
||||||
|
prealloc:
|
||||||
|
simple: false
|
||||||
|
range-loops: false
|
||||||
|
for-loops: false
|
||||||
|
|
||||||
|
whitespace:
|
||||||
|
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||||
|
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- megacheck
|
||||||
|
- govet
|
||||||
|
disable:
|
||||||
|
- maligned
|
||||||
|
- prealloc
|
||||||
|
disable-all: false
|
||||||
|
presets:
|
||||||
|
- bugs
|
||||||
|
- unused
|
||||||
|
fast: false
|
||||||
|
|
@ -4,18 +4,12 @@ git:
|
||||||
depth: 1
|
depth: 1
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
- GO111MODULE=off
|
go: 1.15.x
|
||||||
go: [ 1.10.x, 1.11.x, 1.12.x ]
|
os: linux
|
||||||
os: [ linux, osx, windows ]
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- env: GO111MODULE=on
|
|
||||||
go: 1.10.x
|
|
||||||
install:
|
install:
|
||||||
- if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi
|
- ./travis/install.sh
|
||||||
- if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi
|
|
||||||
script:
|
script:
|
||||||
- export GOMAXPROCS=4
|
- cd ci
|
||||||
- export GORACE=halt_on_error=1
|
- go run mage.go -v -w ../ crossBuild
|
||||||
- go test -race -v ./...
|
- go run mage.go -v -w ../ lint
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi
|
- go run mage.go -v -w ../ test
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,68 @@
|
||||||
|
# 1.8.1
|
||||||
|
Code quality:
|
||||||
|
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
||||||
|
* improve timestamp format documentation
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* fix race condition on logger hooks
|
||||||
|
|
||||||
|
|
||||||
|
# 1.8.0
|
||||||
|
|
||||||
|
Correct versioning number replacing v1.7.1.
|
||||||
|
|
||||||
|
# 1.7.1
|
||||||
|
|
||||||
|
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
||||||
|
|
||||||
|
Code quality:
|
||||||
|
* use go 1.15 in travis
|
||||||
|
* use magefile as task runner
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* small fixes about new go 1.13 error formatting system
|
||||||
|
* Fix for long time race condiction with mutating data hooks
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* build support for zos
|
||||||
|
|
||||||
|
# 1.7.0
|
||||||
|
Fixes:
|
||||||
|
* the dependency toward a windows terminal library has been removed
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* a new buffer pool management API has been added
|
||||||
|
* a set of `<LogLevel>Fn()` functions have been added
|
||||||
|
|
||||||
|
# 1.6.0
|
||||||
|
Fixes:
|
||||||
|
* end of line cleanup
|
||||||
|
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
|
||||||
|
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* add an option to the `TextFormatter` to completely disable fields quoting
|
||||||
|
|
||||||
|
# 1.5.0
|
||||||
|
Code quality:
|
||||||
|
* add golangci linter run on travis
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* add mutex for hooks concurrent access on `Entry` data
|
||||||
|
* caller function field for go1.14
|
||||||
|
* fix build issue for gopherjs target
|
||||||
|
|
||||||
|
Feature:
|
||||||
|
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
|
||||||
|
* add a `DisableHTMLEscape` option in the `JSONFormatter`
|
||||||
|
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
|
||||||
|
|
||||||
|
# 1.4.2
|
||||||
|
* Fixes build break for plan9, nacl, solaris
|
||||||
# 1.4.1
|
# 1.4.1
|
||||||
This new release introduces:
|
This new release introduces:
|
||||||
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
||||||
* Remove dependency on golang.org/x/crypto (#932, #943)
|
* Remove dependency on golang.org/x/crypto (#932, #943)
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
||||||
|
|
@ -9,7 +70,7 @@ Fixes:
|
||||||
# 1.4.0
|
# 1.4.0
|
||||||
This new release introduces:
|
This new release introduces:
|
||||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
||||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
|
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
|
||||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,28 @@
|
||||||
# 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 <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://travis-ci.org/sirupsen/logrus) [](https://pkg.go.dev/github.com/sirupsen/logrus)
|
||||||
|
|
||||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
the standard library logger.
|
the standard library logger.
|
||||||
|
|
||||||
|
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
|
||||||
|
simply too hard to do in a way that won't break many people's projects, which is
|
||||||
|
the last thing you want from your Logging library (again...).
|
||||||
|
|
||||||
|
This does not mean Logrus is dead. Logrus will continue to be maintained for
|
||||||
|
security, (backwards compatible) bug fixes, and performance (where we are
|
||||||
|
limited by the interface).
|
||||||
|
|
||||||
|
I believe Logrus' biggest contribution is to have played a part in today's
|
||||||
|
widespread use of structured logging in Golang. There doesn't seem to be a
|
||||||
|
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
|
||||||
|
community has built those independently. Many fantastic alternatives have sprung
|
||||||
|
up. Logrus would look like those, had it been re-designed with what we know
|
||||||
|
about structured logging in Go today. Check out, for example,
|
||||||
|
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
|
||||||
|
|
||||||
|
[zerolog]: https://github.com/rs/zerolog
|
||||||
|
[zap]: https://github.com/uber-go/zap
|
||||||
|
[apex]: https://github.com/apex/log
|
||||||
|
|
||||||
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
**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,
|
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
|
this caused issues in the community and we needed a standard. Some environments
|
||||||
|
|
@ -15,11 +35,6 @@ comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||||
For an in-depth explanation of the casing issue, see [this
|
For an in-depth explanation of the casing issue, see [this
|
||||||
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
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
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
plain text):
|
plain text):
|
||||||
|
|
||||||
|
|
@ -28,7 +43,7 @@ plain text):
|
||||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||||
or Splunk:
|
or Splunk:
|
||||||
|
|
||||||
```json
|
```text
|
||||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
{"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"}
|
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||||
|
|
||||||
|
|
@ -84,7 +99,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr
|
||||||
```
|
```
|
||||||
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
||||||
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
||||||
environment via benchmarks:
|
environment via benchmarks:
|
||||||
```
|
```
|
||||||
go test -bench=.*CallerTracing
|
go test -bench=.*CallerTracing
|
||||||
```
|
```
|
||||||
|
|
@ -187,7 +202,7 @@ func main() {
|
||||||
log.Out = os.Stdout
|
log.Out = os.Stdout
|
||||||
|
|
||||||
// You could set this to any `io.Writer` such as a file
|
// 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)
|
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
// if err == nil {
|
// if err == nil {
|
||||||
// log.Out = file
|
// log.Out = file
|
||||||
// } else {
|
// } else {
|
||||||
|
|
@ -272,7 +287,7 @@ func init() {
|
||||||
```
|
```
|
||||||
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).
|
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).
|
||||||
|
|
||||||
A list of currently known of service hook can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
||||||
|
|
||||||
|
|
||||||
#### Level logging
|
#### Level logging
|
||||||
|
|
@ -302,6 +317,8 @@ log.SetLevel(log.InfoLevel)
|
||||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||||
environment if your application has that.
|
environment if your application has that.
|
||||||
|
|
||||||
|
Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
|
||||||
|
|
||||||
#### Entries
|
#### Entries
|
||||||
|
|
||||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||||
|
|
@ -326,7 +343,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
init() {
|
func init() {
|
||||||
// do something here to set environment depending on an environment variable
|
// do something here to set environment depending on an environment variable
|
||||||
// or command-line flag
|
// or command-line flag
|
||||||
if Environment == "production" {
|
if Environment == "production" {
|
||||||
|
|
@ -354,6 +371,7 @@ The built-in logging formatters are:
|
||||||
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||||
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
||||||
truncation set the `DisableLevelTruncation` field to `true`.
|
truncation set the `DisableLevelTruncation` field to `true`.
|
||||||
|
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
||||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
||||||
|
|
@ -364,8 +382,10 @@ Third party logging formatters:
|
||||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
||||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
* [`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.
|
* [`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̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
|
||||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
||||||
|
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
|
||||||
|
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
|
||||||
|
|
||||||
You can define your formatter by implementing the `Formatter` interface,
|
You can define your formatter by implementing the `Formatter` interface,
|
||||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||||
|
|
@ -384,7 +404,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
// source of the official loggers.
|
// source of the official loggers.
|
||||||
serialized, err := json.Marshal(entry.Data)
|
serialized, err := json.Marshal(entry.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
||||||
}
|
}
|
||||||
return append(serialized, '\n'), nil
|
return append(serialized, '\n'), nil
|
||||||
}
|
}
|
||||||
|
|
@ -430,14 +450,14 @@ entries. It should not be a feature of the application-level logger.
|
||||||
|
|
||||||
| Tool | Description |
|
| 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 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 be generated with different configs in different environments.|
|
||||||
|[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) |
|
|[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
|
#### Testing
|
||||||
|
|
||||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
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
|
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
|
||||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
@ -465,7 +485,7 @@ func TestSomething(t*testing.T){
|
||||||
|
|
||||||
Logrus can register one or more functions that will be called when any `fatal`
|
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
|
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
|
logrus performs an `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.
|
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.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -490,6 +510,6 @@ Situation when locking is not needed includes:
|
||||||
|
|
||||||
1) logger.Out is protected by locks.
|
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)
|
2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)
|
||||||
|
|
||||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
version: "{build}"
|
version: "{build}"
|
||||||
platform: x64
|
platform: x64
|
||||||
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
||||||
environment:
|
environment:
|
||||||
GOPATH: c:\gopath
|
GOPATH: c:\gopath
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
install:
|
install:
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||||
- go version
|
- go version
|
||||||
build_script:
|
build_script:
|
||||||
- go get -t
|
- go get -t
|
||||||
- go test
|
- go test
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bufferPool BufferPool
|
||||||
|
)
|
||||||
|
|
||||||
|
type BufferPool interface {
|
||||||
|
Put(*bytes.Buffer)
|
||||||
|
Get() *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultPool struct {
|
||||||
|
pool *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *defaultPool) Put(buf *bytes.Buffer) {
|
||||||
|
p.pool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *defaultPool) Get() *bytes.Buffer {
|
||||||
|
return p.pool.Get().(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBufferPool allows to replace the default logrus buffer pool
|
||||||
|
// to better meets the specific needs of an application.
|
||||||
|
func SetBufferPool(bp BufferPool) {
|
||||||
|
bufferPool = bp
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetBufferPool(&defaultPool{
|
||||||
|
pool: &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bufferPool *sync.Pool
|
|
||||||
|
|
||||||
// qualified package name, cached at first use
|
// qualified package name, cached at first use
|
||||||
logrusPackage string
|
logrusPackage string
|
||||||
|
|
@ -31,12 +30,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bufferPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return new(bytes.Buffer)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// start at the bottom of the stack before the package-name cache is primed
|
// start at the bottom of the stack before the package-name cache is primed
|
||||||
minimumCallerDepth = 1
|
minimumCallerDepth = 1
|
||||||
}
|
}
|
||||||
|
|
@ -85,10 +78,23 @@ func NewEntry(logger *Logger) *Entry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Dup() *Entry {
|
||||||
|
data := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the bytes representation of this entry from the formatter.
|
||||||
|
func (entry *Entry) Bytes() ([]byte, error) {
|
||||||
|
return entry.Logger.Formatter.Format(entry)
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the string representation from the reader and ultimately the
|
// Returns the string representation from the reader and ultimately the
|
||||||
// formatter.
|
// formatter.
|
||||||
func (entry *Entry) String() (string, error) {
|
func (entry *Entry) String() (string, error) {
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
serialized, err := entry.Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +109,11 @@ func (entry *Entry) WithError(err error) *Entry {
|
||||||
|
|
||||||
// Add a context to the Entry.
|
// Add a context to the Entry.
|
||||||
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
||||||
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
|
dataCopy := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
dataCopy[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a single field to the Entry.
|
// Add a single field to the Entry.
|
||||||
|
|
@ -121,11 +131,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
for k, v := range fields {
|
for k, v := range fields {
|
||||||
isErrField := false
|
isErrField := false
|
||||||
if t := reflect.TypeOf(v); t != nil {
|
if t := reflect.TypeOf(v); t != nil {
|
||||||
switch t.Kind() {
|
switch {
|
||||||
case reflect.Func:
|
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
||||||
isErrField = true
|
isErrField = true
|
||||||
case reflect.Ptr:
|
|
||||||
isErrField = t.Elem().Kind() == reflect.Func
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isErrField {
|
if isErrField {
|
||||||
|
|
@ -144,7 +152,11 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
|
|
||||||
// Overrides the time of the Entry.
|
// Overrides the time of the Entry.
|
||||||
func (entry *Entry) WithTime(t time.Time) *Entry {
|
func (entry *Entry) WithTime(t time.Time) *Entry {
|
||||||
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
|
dataCopy := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
dataCopy[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPackageName reduces a fully qualified function name to the package name
|
// getPackageName reduces a fully qualified function name to the package name
|
||||||
|
|
@ -165,15 +177,20 @@ func getPackageName(f string) string {
|
||||||
|
|
||||||
// getCaller retrieves the name of the first non-logrus calling function
|
// getCaller retrieves the name of the first non-logrus calling function
|
||||||
func getCaller() *runtime.Frame {
|
func getCaller() *runtime.Frame {
|
||||||
|
|
||||||
// cache this package's fully-qualified name
|
// cache this package's fully-qualified name
|
||||||
callerInitOnce.Do(func() {
|
callerInitOnce.Do(func() {
|
||||||
pcs := make([]uintptr, 2)
|
pcs := make([]uintptr, maximumCallerDepth)
|
||||||
_ = runtime.Callers(0, pcs)
|
_ = runtime.Callers(0, pcs)
|
||||||
logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
|
|
||||||
|
|
||||||
// now that we have the cache, we can skip a minimum count of known-logrus functions
|
// dynamic get the package name and the minimum caller depth
|
||||||
// XXX this is dubious, the number of frames may vary
|
for i := 0; i < maximumCallerDepth; i++ {
|
||||||
|
funcName := runtime.FuncForPC(pcs[i]).Name()
|
||||||
|
if strings.Contains(funcName, "getCaller") {
|
||||||
|
logrusPackage = getPackageName(funcName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
minimumCallerDepth = knownLogrusFrames
|
minimumCallerDepth = knownLogrusFrames
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -187,7 +204,7 @@ func getCaller() *runtime.Frame {
|
||||||
|
|
||||||
// If the caller isn't part of this package, we're done
|
// If the caller isn't part of this package, we're done
|
||||||
if pkg != logrusPackage {
|
if pkg != logrusPackage {
|
||||||
return &f
|
return &f //nolint:scopelint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,49 +218,66 @@ func (entry Entry) HasCaller() (has bool) {
|
||||||
entry.Caller != nil
|
entry.Caller != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is not declared with a pointer value because otherwise
|
func (entry *Entry) log(level Level, msg string) {
|
||||||
// race conditions will occur when using multiple goroutines
|
|
||||||
func (entry Entry) log(level Level, msg string) {
|
|
||||||
var buffer *bytes.Buffer
|
var buffer *bytes.Buffer
|
||||||
|
|
||||||
// Default to now, but allow users to override if they want.
|
newEntry := entry.Dup()
|
||||||
//
|
|
||||||
// We don't have to worry about polluting future calls to Entry#log()
|
if newEntry.Time.IsZero() {
|
||||||
// with this assignment because this function is declared with a
|
newEntry.Time = time.Now()
|
||||||
// non-pointer receiver.
|
|
||||||
if entry.Time.IsZero() {
|
|
||||||
entry.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Level = level
|
newEntry.Level = level
|
||||||
entry.Message = msg
|
newEntry.Message = msg
|
||||||
if entry.Logger.ReportCaller {
|
|
||||||
entry.Caller = getCaller()
|
newEntry.Logger.mu.Lock()
|
||||||
|
reportCaller := newEntry.Logger.ReportCaller
|
||||||
|
bufPool := newEntry.getBufferPool()
|
||||||
|
newEntry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
if reportCaller {
|
||||||
|
newEntry.Caller = getCaller()
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.fireHooks()
|
newEntry.fireHooks()
|
||||||
|
buffer = bufPool.Get()
|
||||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
defer func() {
|
||||||
|
newEntry.Buffer = nil
|
||||||
|
buffer.Reset()
|
||||||
|
bufPool.Put(buffer)
|
||||||
|
}()
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
defer bufferPool.Put(buffer)
|
newEntry.Buffer = buffer
|
||||||
entry.Buffer = buffer
|
|
||||||
|
|
||||||
entry.write()
|
newEntry.write()
|
||||||
|
|
||||||
entry.Buffer = nil
|
newEntry.Buffer = nil
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
// 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
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
// directly here.
|
// directly here.
|
||||||
if level <= PanicLevel {
|
if level <= PanicLevel {
|
||||||
panic(&entry)
|
panic(newEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) getBufferPool() (pool BufferPool) {
|
||||||
|
if entry.Logger.BufferPool != nil {
|
||||||
|
return entry.Logger.BufferPool
|
||||||
|
}
|
||||||
|
return bufferPool
|
||||||
|
}
|
||||||
|
|
||||||
func (entry *Entry) fireHooks() {
|
func (entry *Entry) fireHooks() {
|
||||||
|
var tmpHooks LevelHooks
|
||||||
entry.Logger.mu.Lock()
|
entry.Logger.mu.Lock()
|
||||||
defer entry.Logger.mu.Unlock()
|
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
||||||
err := entry.Logger.Hooks.Fire(entry.Level, entry)
|
for k, v := range entry.Logger.Hooks {
|
||||||
|
tmpHooks[k] = v
|
||||||
|
}
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
err := tmpHooks.Fire(entry.Level, entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
@ -255,14 +289,16 @@ func (entry *Entry) write() {
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
} else {
|
return
|
||||||
_, err = entry.Logger.Out.Write(serialized)
|
}
|
||||||
if err != nil {
|
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log will log a message at the level given as parameter.
|
||||||
|
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||||
|
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
|
||||||
func (entry *Entry) Log(level Level, args ...interface{}) {
|
func (entry *Entry) Log(level Level, args ...interface{}) {
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
if entry.Logger.IsLevelEnabled(level) {
|
||||||
entry.log(level, fmt.Sprint(args...))
|
entry.log(level, fmt.Sprint(args...))
|
||||||
|
|
@ -304,7 +340,6 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
entry.Log(PanicLevel, args...)
|
entry.Log(PanicLevel, args...)
|
||||||
panic(fmt.Sprint(args...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry Printf family functions
|
// Entry Printf family functions
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ func WithFields(fields Fields) *Entry {
|
||||||
return std.WithFields(fields)
|
return std.WithFields(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTime creats an entry from the standard logger and overrides the time of
|
// WithTime creates an entry from the standard logger and overrides the time of
|
||||||
// logs generated with it.
|
// logs generated with it.
|
||||||
//
|
//
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
|
@ -134,6 +134,51 @@ func Fatal(args ...interface{}) {
|
||||||
std.Fatal(args...)
|
std.Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraceFn logs a message from a func at level Trace on the standard logger.
|
||||||
|
func TraceFn(fn LogFunction) {
|
||||||
|
std.TraceFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugFn logs a message from a func at level Debug on the standard logger.
|
||||||
|
func DebugFn(fn LogFunction) {
|
||||||
|
std.DebugFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFn logs a message from a func at level Info on the standard logger.
|
||||||
|
func PrintFn(fn LogFunction) {
|
||||||
|
std.PrintFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoFn logs a message from a func at level Info on the standard logger.
|
||||||
|
func InfoFn(fn LogFunction) {
|
||||||
|
std.InfoFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WarnFn logs a message from a func at level Warn on the standard logger.
|
||||||
|
func WarnFn(fn LogFunction) {
|
||||||
|
std.WarnFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WarningFn logs a message from a func at level Warn on the standard logger.
|
||||||
|
func WarningFn(fn LogFunction) {
|
||||||
|
std.WarningFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorFn logs a message from a func at level Error on the standard logger.
|
||||||
|
func ErrorFn(fn LogFunction) {
|
||||||
|
std.ErrorFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicFn logs a message from a func at level Panic on the standard logger.
|
||||||
|
func PanicFn(fn LogFunction) {
|
||||||
|
std.PanicFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func FatalFn(fn LogFunction) {
|
||||||
|
std.FatalFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
// Tracef logs a message at level Trace on the standard logger.
|
// Tracef logs a message at level Trace on the standard logger.
|
||||||
func Tracef(format string, args ...interface{}) {
|
func Tracef(format string, args ...interface{}) {
|
||||||
std.Tracef(format, args...)
|
std.Tracef(format, args...)
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,17 @@ func (f FieldMap) resolve(key fieldKey) string {
|
||||||
// JSONFormatter formats logs into parsable json
|
// JSONFormatter formats logs into parsable json
|
||||||
type JSONFormatter struct {
|
type JSONFormatter struct {
|
||||||
// TimestampFormat sets the format used for marshaling timestamps.
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
TimestampFormat string
|
TimestampFormat string
|
||||||
|
|
||||||
// DisableTimestamp allows disabling automatic timestamps in output
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
DisableTimestamp bool
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// DisableHTMLEscape allows disabling html escaping in output
|
||||||
|
DisableHTMLEscape bool
|
||||||
|
|
||||||
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
||||||
DataKey string
|
DataKey string
|
||||||
|
|
||||||
|
|
@ -110,11 +116,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder := json.NewEncoder(b)
|
encoder := json.NewEncoder(b)
|
||||||
|
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
||||||
if f.PrettyPrint {
|
if f.PrettyPrint {
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
}
|
}
|
||||||
if err := encoder.Encode(data); err != nil {
|
if err := encoder.Encode(data); err != nil {
|
||||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
|
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LogFunction For big messages, it can be more efficient to pass a function
|
||||||
|
// and only call it if the log level is actually enables rather than
|
||||||
|
// generating the log message and then checking if the level is enabled
|
||||||
|
type LogFunction func() []interface{}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
// 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
|
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||||
|
|
@ -39,6 +44,9 @@ type Logger struct {
|
||||||
entryPool sync.Pool
|
entryPool sync.Pool
|
||||||
// Function to exit the application, defaults to `os.Exit()`
|
// Function to exit the application, defaults to `os.Exit()`
|
||||||
ExitFunc exitFunc
|
ExitFunc exitFunc
|
||||||
|
// The buffer pool used to format the log. If it is nil, the default global
|
||||||
|
// buffer pool will be used.
|
||||||
|
BufferPool BufferPool
|
||||||
}
|
}
|
||||||
|
|
||||||
type exitFunc func(int)
|
type exitFunc func(int)
|
||||||
|
|
@ -68,10 +76,10 @@ func (mw *MutexWrap) Disable() {
|
||||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||||
// instantiate your own:
|
// instantiate your own:
|
||||||
//
|
//
|
||||||
// var log = &Logger{
|
// var log = &logrus.Logger{
|
||||||
// Out: os.Stderr,
|
// Out: os.Stderr,
|
||||||
// Formatter: new(JSONFormatter),
|
// Formatter: new(logrus.TextFormatter),
|
||||||
// Hooks: make(LevelHooks),
|
// Hooks: make(logrus.LevelHooks),
|
||||||
// Level: logrus.DebugLevel,
|
// Level: logrus.DebugLevel,
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
|
@ -100,8 +108,9 @@ func (logger *Logger) releaseEntry(entry *Entry) {
|
||||||
logger.entryPool.Put(entry)
|
logger.entryPool.Put(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a field to the log entry, note that it doesn't log until you call
|
// WithField allocates a new entry and adds a field to it.
|
||||||
// Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry.
|
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
||||||
|
// this new returned entry.
|
||||||
// If you want multiple fields, use `WithFields`.
|
// If you want multiple fields, use `WithFields`.
|
||||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
entry := logger.newEntry()
|
entry := logger.newEntry()
|
||||||
|
|
@ -186,6 +195,9 @@ func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
logger.Logf(PanicLevel, format, args...)
|
logger.Logf(PanicLevel, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log will log a message at the level given as parameter.
|
||||||
|
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||||
|
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
||||||
func (logger *Logger) Log(level Level, args ...interface{}) {
|
func (logger *Logger) Log(level Level, args ...interface{}) {
|
||||||
if logger.IsLevelEnabled(level) {
|
if logger.IsLevelEnabled(level) {
|
||||||
entry := logger.newEntry()
|
entry := logger.newEntry()
|
||||||
|
|
@ -194,6 +206,14 @@ func (logger *Logger) Log(level Level, args ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) LogFn(level Level, fn LogFunction) {
|
||||||
|
if logger.IsLevelEnabled(level) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Log(level, fn()...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (logger *Logger) Trace(args ...interface{}) {
|
func (logger *Logger) Trace(args ...interface{}) {
|
||||||
logger.Log(TraceLevel, args...)
|
logger.Log(TraceLevel, args...)
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +253,45 @@ func (logger *Logger) Panic(args ...interface{}) {
|
||||||
logger.Log(PanicLevel, args...)
|
logger.Log(PanicLevel, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) TraceFn(fn LogFunction) {
|
||||||
|
logger.LogFn(TraceLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) DebugFn(fn LogFunction) {
|
||||||
|
logger.LogFn(DebugLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) InfoFn(fn LogFunction) {
|
||||||
|
logger.LogFn(InfoLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) PrintFn(fn LogFunction) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Print(fn()...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WarnFn(fn LogFunction) {
|
||||||
|
logger.LogFn(WarnLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WarningFn(fn LogFunction) {
|
||||||
|
logger.WarnFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) ErrorFn(fn LogFunction) {
|
||||||
|
logger.LogFn(ErrorLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) FatalFn(fn LogFunction) {
|
||||||
|
logger.LogFn(FatalLevel, fn)
|
||||||
|
logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) PanicFn(fn LogFunction) {
|
||||||
|
logger.LogFn(PanicLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
||||||
if logger.IsLevelEnabled(level) {
|
if logger.IsLevelEnabled(level) {
|
||||||
entry := logger.newEntry()
|
entry := logger.newEntry()
|
||||||
|
|
@ -349,3 +408,10 @@ func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
|
||||||
logger.mu.Unlock()
|
logger.mu.Unlock()
|
||||||
return oldHooks
|
return oldHooks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBufferPool sets the logger buffer pool.
|
||||||
|
func (logger *Logger) SetBufferPool(pool BufferPool) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.BufferPool = pool
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ func (level *Level) UnmarshalText(text []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*level = Level(l)
|
*level = l
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
// +build !js
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
|
@ -10,4 +11,3 @@ func isTerminal(fd int) bool {
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
import (
|
func isTerminal(fd int) bool {
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
tests/tools/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
11
tests/tools/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build js nacl plan9
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// +build !appengine,!js,!windows
|
// +build !appengine,!js,!windows,!nacl,!plan9
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
|
|
||||||
11
tests/tools/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
Normal file
11
tests/tools/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build linux aix
|
// +build linux aix zos
|
||||||
|
// +build !js
|
||||||
|
|
||||||
package logrus
|
package logrus
|
||||||
|
|
||||||
|
|
@ -10,4 +11,3 @@ func isTerminal(fd int) bool {
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,23 @@ package logrus
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
switch v := w.(type) {
|
switch v := w.(type) {
|
||||||
case *os.File:
|
case *os.File:
|
||||||
|
handle := windows.Handle(v.Fd())
|
||||||
var mode uint32
|
var mode uint32
|
||||||
err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode)
|
if err := windows.GetConsoleMode(handle, &mode); err != nil {
|
||||||
return err == nil
|
return false
|
||||||
default:
|
}
|
||||||
return false
|
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
if err := windows.SetConsoleMode(handle, mode); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func initTerminal(w io.Writer) {
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// +build !appengine,!js,windows
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
sequences "github.com/konsorten/go-windows-terminal-sequences"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initTerminal(w io.Writer) {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -32,6 +34,14 @@ type TextFormatter struct {
|
||||||
// Force disabling colors.
|
// Force disabling colors.
|
||||||
DisableColors bool
|
DisableColors bool
|
||||||
|
|
||||||
|
// Force quoting of all values
|
||||||
|
ForceQuote bool
|
||||||
|
|
||||||
|
// DisableQuote disables quoting for all values.
|
||||||
|
// DisableQuote will have a lower priority than ForceQuote.
|
||||||
|
// If both of them are set to true, quote will be forced on all values.
|
||||||
|
DisableQuote bool
|
||||||
|
|
||||||
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
||||||
EnvironmentOverrideColors bool
|
EnvironmentOverrideColors bool
|
||||||
|
|
||||||
|
|
@ -43,7 +53,10 @@ type TextFormatter struct {
|
||||||
// the time passed since beginning of execution.
|
// the time passed since beginning of execution.
|
||||||
FullTimestamp bool
|
FullTimestamp bool
|
||||||
|
|
||||||
// TimestampFormat to use for display when a full timestamp is printed
|
// TimestampFormat to use for display when a full timestamp is printed.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
TimestampFormat string
|
TimestampFormat string
|
||||||
|
|
||||||
// The fields are sorted by default for a consistent output. For applications
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
|
@ -57,6 +70,10 @@ type TextFormatter struct {
|
||||||
// Disables the truncation of the level text to 4 characters.
|
// Disables the truncation of the level text to 4 characters.
|
||||||
DisableLevelTruncation bool
|
DisableLevelTruncation bool
|
||||||
|
|
||||||
|
// PadLevelText Adds padding the level text so that all the levels output at the same length
|
||||||
|
// PadLevelText is a superset of the DisableLevelTruncation option
|
||||||
|
PadLevelText bool
|
||||||
|
|
||||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||||
QuoteEmptyFields bool
|
QuoteEmptyFields bool
|
||||||
|
|
||||||
|
|
@ -79,14 +96,20 @@ type TextFormatter struct {
|
||||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||||
|
|
||||||
terminalInitOnce sync.Once
|
terminalInitOnce sync.Once
|
||||||
|
|
||||||
|
// The max length of the level text, generated dynamically on init
|
||||||
|
levelTextMaxLength int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) init(entry *Entry) {
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
if entry.Logger != nil {
|
if entry.Logger != nil {
|
||||||
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||||
|
}
|
||||||
if f.isTerminal {
|
// Get the max length of the level text
|
||||||
initTerminal(entry.Logger.Out)
|
for _, level := range AllLevels {
|
||||||
|
levelTextLength := utf8.RuneCount([]byte(level.String()))
|
||||||
|
if levelTextLength > f.levelTextMaxLength {
|
||||||
|
f.levelTextMaxLength = levelTextLength
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,11 +118,10 @@ func (f *TextFormatter) isColored() bool {
|
||||||
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
||||||
|
|
||||||
if f.EnvironmentOverrideColors {
|
if f.EnvironmentOverrideColors {
|
||||||
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
|
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
|
||||||
|
case ok && force != "0":
|
||||||
isColored = true
|
isColored = true
|
||||||
} else if ok && force == "0" {
|
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
|
||||||
isColored = false
|
|
||||||
} else if os.Getenv("CLICOLOR") == "0" {
|
|
||||||
isColored = false
|
isColored = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,14 +238,25 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||||
levelColor = yellow
|
levelColor = yellow
|
||||||
case ErrorLevel, FatalLevel, PanicLevel:
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
levelColor = red
|
levelColor = red
|
||||||
|
case InfoLevel:
|
||||||
|
levelColor = blue
|
||||||
default:
|
default:
|
||||||
levelColor = blue
|
levelColor = blue
|
||||||
}
|
}
|
||||||
|
|
||||||
levelText := strings.ToUpper(entry.Level.String())
|
levelText := strings.ToUpper(entry.Level.String())
|
||||||
if !f.DisableLevelTruncation {
|
if !f.DisableLevelTruncation && !f.PadLevelText {
|
||||||
levelText = levelText[0:4]
|
levelText = levelText[0:4]
|
||||||
}
|
}
|
||||||
|
if f.PadLevelText {
|
||||||
|
// Generates the format string used in the next line, for example "%-6s" or "%-7s".
|
||||||
|
// Based on the max level text length.
|
||||||
|
formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
|
||||||
|
// Formats the level text by appending spaces up to the max length, for example:
|
||||||
|
// - "INFO "
|
||||||
|
// - "WARNING"
|
||||||
|
levelText = fmt.Sprintf(formatString, levelText)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove a single newline if it already exists in the message to keep
|
// Remove a single newline if it already exists in the message to keep
|
||||||
// the behavior of logrus text_formatter the same as the stdlib log package
|
// the behavior of logrus text_formatter the same as the stdlib log package
|
||||||
|
|
@ -247,11 +280,12 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.DisableTimestamp {
|
switch {
|
||||||
|
case f.DisableTimestamp:
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
||||||
} else if !f.FullTimestamp {
|
case !f.FullTimestamp:
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
||||||
} else {
|
default:
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
||||||
}
|
}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
|
|
@ -262,9 +296,15 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||||
|
if f.ForceQuote {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if f.QuoteEmptyFields && len(text) == 0 {
|
if f.QuoteEmptyFields && len(text) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if f.DisableQuote {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for _, ch := range text {
|
for _, ch := range text {
|
||||||
if !((ch >= 'a' && ch <= 'z') ||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
(ch >= 'A' && ch <= 'Z') ||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,35 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Writer at INFO level. See WriterLevel for details.
|
||||||
func (logger *Logger) Writer() *io.PipeWriter {
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
return logger.WriterLevel(InfoLevel)
|
return logger.WriterLevel(InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriterLevel returns an io.Writer that can be used to write arbitrary text to
|
||||||
|
// the logger at the given log level. Each line written to the writer will be
|
||||||
|
// printed in the usual way using formatters and hooks. The writer is part of an
|
||||||
|
// io.Pipe and it is the callers responsibility to close the writer when done.
|
||||||
|
// This can be used to override the standard library logger easily.
|
||||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
return NewEntry(logger).WriterLevel(level)
|
return NewEntry(logger).WriterLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writer returns an io.Writer that writes to the logger at the info log level
|
||||||
func (entry *Entry) Writer() *io.PipeWriter {
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
return entry.WriterLevel(InfoLevel)
|
return entry.WriterLevel(InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriterLevel returns an io.Writer that writes to the logger at the given log level
|
||||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
var printFunc func(args ...interface{})
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
// Determine which log function to use based on the specified log level
|
||||||
switch level {
|
switch level {
|
||||||
case TraceLevel:
|
case TraceLevel:
|
||||||
printFunc = entry.Trace
|
printFunc = entry.Trace
|
||||||
|
|
@ -42,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
printFunc = entry.Print
|
printFunc = entry.Print
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start a new goroutine to scan the input and write it to the logger using the specified print function.
|
||||||
|
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
|
||||||
go entry.writerScanner(reader, printFunc)
|
go entry.writerScanner(reader, printFunc)
|
||||||
|
|
||||||
|
// Set a finalizer function to close the writer when it is garbage collected
|
||||||
runtime.SetFinalizer(writer, writerFinalizer)
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writerScanner scans the input from the reader and writes it to the logger
|
||||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
for scanner.Scan() {
|
|
||||||
printFunc(scanner.Text())
|
// Set the buffer size to the maximum token size to avoid buffer overflows
|
||||||
|
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
|
||||||
|
|
||||||
|
// Define a split function to split the input into chunks of up to 64KB
|
||||||
|
chunkSize := bufio.MaxScanTokenSize // 64KB
|
||||||
|
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
|
||||||
|
if len(data) >= chunkSize {
|
||||||
|
return chunkSize, data[:chunkSize], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufio.ScanLines(data, atEOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the custom split function to split the input
|
||||||
|
scanner.Split(splitFunc)
|
||||||
|
|
||||||
|
// Scan the input and write it to the logger using the specified print function
|
||||||
|
for scanner.Scan() {
|
||||||
|
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was an error while scanning the input, log an error
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
entry.Errorf("Error while reading from Writer: %s", err)
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the reader when we are done
|
||||||
reader.Close()
|
reader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
|
||||||
func writerFinalizer(writer *io.PipeWriter) {
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
writer.Close()
|
writer.Close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,7 @@ go_import_path: github.com/vbatts/git-validation
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- "tip"
|
- "tip"
|
||||||
- "1.x"
|
- "1.13.x"
|
||||||
- "1.11.x"
|
|
||||||
- "1.10.x"
|
|
||||||
- "1.9.x"
|
|
||||||
- "1.8.x"
|
|
||||||
- "1.7.x"
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
||||||
|
|
@ -29,10 +24,8 @@ before_script:
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get ./...
|
- go get ./...
|
||||||
- if [[ "$(go version |awk '{ print $3 }')" =~ ^go1\.11\. ]] ; then go get -u golang.org/x/lint/golint ; fi
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- if [[ "$(go version |awk '{ print $3 }')" =~ ^go1\.11\. ]] ; then golint -set_exit_status ./... ; fi
|
|
||||||
- go vet -x ./...
|
- go vet -x ./...
|
||||||
- go build .
|
- go build .
|
||||||
- go test -v ./...
|
- go test -v ./...
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
# git-validation
|
# git-validation
|
||||||
|
|
||||||
A way to do validation on git commits.
|
A way to do validation on git commits.
|
||||||
[](https://travis-ci.org/vbatts/git-validation)
|
[](https://travis-ci.org/vbatts/git-validation)
|
||||||
|
[](https://github.com/vbatts/git-validation/actions/workflows/go.yml)
|
||||||
|
|
||||||
## install
|
## install
|
||||||
|
|
||||||
```console
|
```shell
|
||||||
vbatts@valse ~ (master) $ go get -u github.com/vbatts/git-validation
|
go install github.com/vbatts/git-validation@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## usage
|
## usage
|
||||||
|
|
||||||
The flags
|
The flags
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master *) $ git-validation -h
|
vbatts@valse ~/src/vb/git-validation (master *) $ git-validation -h
|
||||||
Usage of git-validation:
|
Usage of git-validation:
|
||||||
-D debug output
|
-D debug output
|
||||||
|
|
@ -28,7 +30,8 @@ Usage of git-validation:
|
||||||
```
|
```
|
||||||
|
|
||||||
The entire default rule set is run by default:
|
The entire default rule set is run by default:
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -list-rules
|
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -list-rules
|
||||||
"dangling-whitespace" -- checking the presence of dangling whitespaces on line endings
|
"dangling-whitespace" -- checking the presence of dangling whitespaces on line endings
|
||||||
"DCO" -- makes sure the commits are signed
|
"DCO" -- makes sure the commits are signed
|
||||||
|
|
@ -37,7 +40,8 @@ vbatts@valse ~/src/vb/git-validation (master) $ git-validation -list-rules
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, specify comma-delimited rules to run:
|
Or, specify comma-delimited rules to run:
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -run DCO,short-subject
|
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -run DCO,short-subject
|
||||||
* b243ca4 "README: adding install and usage" ... PASS
|
* b243ca4 "README: adding install and usage" ... PASS
|
||||||
* d614ccf "*: run tests in a runner" ... PASS
|
* d614ccf "*: run tests in a runner" ... PASS
|
||||||
|
|
@ -49,7 +53,8 @@ vbatts@valse ~/src/vb/git-validation (master) $ git-validation -run DCO,short-su
|
||||||
```
|
```
|
||||||
|
|
||||||
Verbosity shows each rule's output:
|
Verbosity shows each rule's output:
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -v
|
vbatts@valse ~/src/vb/git-validation (master) $ git-validation -v
|
||||||
* d614ccf "*: run tests in a runner" ... PASS
|
* d614ccf "*: run tests in a runner" ... PASS
|
||||||
- PASS - has a valid DCO
|
- PASS - has a valid DCO
|
||||||
|
|
@ -72,7 +77,8 @@ vbatts@valse ~/src/vb/git-validation (master) $ git-validation -v
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's a failure:
|
Here's a failure:
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master) $ git-validation
|
vbatts@valse ~/src/vb/git-validation (master) $ git-validation
|
||||||
* 49f51a8 "README: adding install and usage" ... FAIL
|
* 49f51a8 "README: adding install and usage" ... FAIL
|
||||||
- FAIL - does not have a valid DCO
|
- FAIL - does not have a valid DCO
|
||||||
|
|
@ -88,12 +94,21 @@ vbatts@valse ~/src/vb/git-validation (master) $ echo $?
|
||||||
```
|
```
|
||||||
|
|
||||||
Excluding paths that are out of the scope of your project:
|
Excluding paths that are out of the scope of your project:
|
||||||
```console
|
|
||||||
|
```shell
|
||||||
vbatts@valse ~/src/vb/git-validation (master) $ GIT_CHECK_EXCLUDE="./vendor:./git/testdata" git-validation -q -run dangling-whitespace
|
vbatts@valse ~/src/vb/git-validation (master) $ GIT_CHECK_EXCLUDE="./vendor:./git/testdata" git-validation -q -run dangling-whitespace
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
using the `GIT_CHECK_EXCLUDE` environment variable. Multiple paths should be separated by colon(`:`)
|
using the `GIT_CHECK_EXCLUDE` environment variable. Multiple paths should be separated by colon(`:`)
|
||||||
|
|
||||||
|
## contributing
|
||||||
|
|
||||||
|
When making a change, verify it with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go run mage.go lint vet build test
|
||||||
|
```
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
|
|
@ -103,4 +118,3 @@ See [`./rules/`](./rules/).
|
||||||
Feel free to contribute more.
|
Feel free to contribute more.
|
||||||
|
|
||||||
Otherwise, by using `validate` package API directly, rules can be handed directly to the `validate.Runner`.
|
Otherwise, by using `validate` package API directly, rules can be handed directly to the `validate.Runner`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,19 @@ import (
|
||||||
// If commitrange is a single commit, all ancestor commits up through the hash provided.
|
// If commitrange is a single commit, all ancestor commits up through the hash provided.
|
||||||
// If commitrange is an empty commit range, then nil is returned.
|
// If commitrange is an empty commit range, then nil is returned.
|
||||||
func Commits(commitrange string) ([]CommitEntry, error) {
|
func Commits(commitrange string) ([]CommitEntry, error) {
|
||||||
cmdArgs := []string{"git", "--no-pager", "log", `--pretty=format:%H`, commitrange}
|
// TEST if it has _enough_ of a range from rev-list
|
||||||
|
commitrange, err := checkRevList(commitrange)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to validate the git commit range: %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmdArgs := []string{"git", "rev-list", commitrange}
|
||||||
if debug() {
|
if debug() {
|
||||||
logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
||||||
}
|
}
|
||||||
output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
|
output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("mm[git] cmd: %q", strings.Join(cmdArgs, " "))
|
logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(output) == 0 {
|
if len(output) == 0 {
|
||||||
|
|
@ -39,6 +45,39 @@ func Commits(commitrange string) ([]CommitEntry, error) {
|
||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the commitrange requested may be longer than the depth being cloned in CI,
|
||||||
|
// check for an error, if so do a git log to get the oldest available commit for a reduced range.
|
||||||
|
func checkRevList(commitrange string) (string, error) {
|
||||||
|
cmdArgs := []string{"git", "rev-list", commitrange}
|
||||||
|
if debug() {
|
||||||
|
logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
||||||
|
}
|
||||||
|
_, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
|
||||||
|
if err == nil {
|
||||||
|
// no issues, return now
|
||||||
|
return commitrange, nil
|
||||||
|
}
|
||||||
|
cmdArgs = []string{"git", "log", "--pretty=oneline", "--no-show-signature"}
|
||||||
|
if debug() {
|
||||||
|
logrus.Infof("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
||||||
|
}
|
||||||
|
output, err := exec.Command(cmdArgs[0], cmdArgs[1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("[git] cmd: %q", strings.Join(cmdArgs, " "))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// This "output" is now the list of available commits and short description.
|
||||||
|
// We want the last commit hash only.. (i.e. `| tail -n1 | awk '{ print $1 }'`)
|
||||||
|
chunks := strings.Split(strings.TrimSpace(string(output)), "\n")
|
||||||
|
if len(chunks) == 1 {
|
||||||
|
return strings.Split(chunks[0], " ")[0], nil
|
||||||
|
}
|
||||||
|
last := chunks[len(chunks)-1]
|
||||||
|
lastCommit := strings.Split(last, " ")[0]
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s..HEAD", lastCommit), nil
|
||||||
|
}
|
||||||
|
|
||||||
// FieldNames are for the formating and rendering of the CommitEntry structs.
|
// FieldNames are for the formating and rendering of the CommitEntry structs.
|
||||||
// Keys here are from git log pretty format "format:..."
|
// Keys here are from git log pretty format "format:..."
|
||||||
var FieldNames = map[string]string{
|
var FieldNames = map[string]string{
|
||||||
|
|
@ -96,6 +135,15 @@ func gitVersionNewerThan(otherV string) (bool, error) {
|
||||||
// Check warns if changes introduce whitespace errors.
|
// Check warns if changes introduce whitespace errors.
|
||||||
// Returns non-zero if any issues are found.
|
// Returns non-zero if any issues are found.
|
||||||
func Check(commit string) ([]byte, error) {
|
func Check(commit string) ([]byte, error) {
|
||||||
|
logs, err := LogUntil(commit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(logs) == 1 {
|
||||||
|
logrus.Debugf("[git.Check] %q is the initial commit. Skipping.", commit)
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--no-pager", "log", "--check",
|
"--no-pager", "log", "--check",
|
||||||
fmt.Sprintf("%s^..%s", commit, commit),
|
fmt.Sprintf("%s^..%s", commit, commit),
|
||||||
|
|
@ -108,6 +156,9 @@ func Check(commit string) ([]byte, error) {
|
||||||
if gitNewEnough {
|
if gitNewEnough {
|
||||||
excludeList := strings.Split(excludeEnvList, ":")
|
excludeList := strings.Split(excludeEnvList, ":")
|
||||||
for _, exclude := range excludeList {
|
for _, exclude := range excludeList {
|
||||||
|
if exclude == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
args = append(args, "--", ".", fmt.Sprintf(":(exclude)%s", exclude))
|
args = append(args, "--", ".", fmt.Sprintf(":(exclude)%s", exclude))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +187,21 @@ func Show(commit string) ([]byte, error) {
|
||||||
// See also FieldNames
|
// See also FieldNames
|
||||||
type CommitEntry map[string]string
|
type CommitEntry map[string]string
|
||||||
|
|
||||||
|
// LogUntil returns the array of oneline commits from initial until the provided commit
|
||||||
|
func LogUntil(commit string) ([]string, error) {
|
||||||
|
cmd := exec.Command("git", "--no-pager", "log", "--oneline", "--no-show-signature", commit)
|
||||||
|
if debug() {
|
||||||
|
logrus.Infof("[git] cmd: %q", strings.Join(cmd.Args, " "))
|
||||||
|
}
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("[git] cmd: %q", strings.Join(cmd.Args, " "))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return strings.Split(strings.TrimSpace(string(out)), "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// LogCommit assembles the full information on a commit from its commit hash
|
// LogCommit assembles the full information on a commit from its commit hash
|
||||||
func LogCommit(commit string) (*CommitEntry, error) {
|
func LogCommit(commit string) (*CommitEntry, error) {
|
||||||
c := CommitEntry{}
|
c := CommitEntry{}
|
||||||
|
|
@ -150,7 +216,8 @@ func LogCommit(commit string) (*CommitEntry, error) {
|
||||||
logrus.Errorf("[git] cmd: %q", strings.Join(cmd.Args, " "))
|
logrus.Errorf("[git] cmd: %q", strings.Join(cmd.Args, " "))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c[v] = strings.TrimSpace(string(out))
|
commitMessage := strings.ReplaceAll(string(out), "\r\n", "\n")
|
||||||
|
c[v] = strings.TrimSpace(commitMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &c, nil
|
return &c, nil
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
//go:build mage
|
||||||
|
// +build mage
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/magefile/mage/mg" // mg contains helpful utility functions, like Deps
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Default target to run when none is specified
|
||||||
|
// If not set, running mage will list available targets
|
||||||
|
Default = Build
|
||||||
|
app string = "git-validation"
|
||||||
|
Stdout = cw{c: color.New(color.FgGreen), o: os.Stdout}
|
||||||
|
Stderr = cw{c: color.New(color.FgRed), o: os.Stderr}
|
||||||
|
)
|
||||||
|
|
||||||
|
// hack around color.Color not implementing Write()
|
||||||
|
type cw struct {
|
||||||
|
c *color.Color
|
||||||
|
o io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw cw) Write(p []byte) (int, error) {
|
||||||
|
i := len(p)
|
||||||
|
_, err := cw.c.Fprint(cw.o, string(p)) // discarding the number of bytes written for now...
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A build step that requires additional params, or platform specific steps for example
|
||||||
|
func Build() error {
|
||||||
|
mg.Deps(InstallDeps)
|
||||||
|
fmt.Println("Building...")
|
||||||
|
cmd := exec.Command("go", "build", "-v", "-o", app, ".")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vet the codes
|
||||||
|
func Vet() error {
|
||||||
|
fmt.Println("go vet...")
|
||||||
|
cmd := exec.Command("go", "vet", "./...")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the Linters
|
||||||
|
func Lint() error {
|
||||||
|
mg.Deps(InstallToolsLint)
|
||||||
|
fmt.Println("Linting...")
|
||||||
|
cmd := exec.Command("golangci-lint", "run", "--skip-dirs", "(^|/).gvm/gos($|/)", "--skip-dirs", "(^|/)go/pkg/mod($|/)")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the tests available
|
||||||
|
func Test() error {
|
||||||
|
fmt.Println("Testing...")
|
||||||
|
cmd := exec.Command("go", "test", "-v", "./...")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom install step if you need your bin someplace other than go/bin
|
||||||
|
func Install() error {
|
||||||
|
mg.Deps(Build)
|
||||||
|
fmt.Println("Installing...")
|
||||||
|
return os.Rename(app, "/usr/local/bin/"+app)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manage your deps, or running package managers.
|
||||||
|
func InstallDeps() error {
|
||||||
|
mg.Deps(Tidy)
|
||||||
|
fmt.Println("Installing Deps...")
|
||||||
|
cmd := exec.Command("go", "get", "./...")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tools used during build/dev/test
|
||||||
|
func InstallTools() error {
|
||||||
|
mg.Deps(InstallToolsLint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstallToolsLint() error {
|
||||||
|
fmt.Println("Installing Deps...")
|
||||||
|
cmd := exec.Command("go", "install", "github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tidy go modules
|
||||||
|
func Tidy() error {
|
||||||
|
fmt.Println("Tidy up...")
|
||||||
|
cmd := exec.Command("go", "mod", "tidy")
|
||||||
|
cmd.Stdout = Stdout
|
||||||
|
cmd.Stderr = Stderr
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up after yourself
|
||||||
|
func Clean() {
|
||||||
|
fmt.Println("Cleaning...")
|
||||||
|
os.RemoveAll(app)
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,10 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
_ "github.com/vbatts/git-validation/rules/danglingwhitespace"
|
_ "github.com/vbatts/git-validation/rules/danglingwhitespace"
|
||||||
_ "github.com/vbatts/git-validation/rules/dco"
|
_ "github.com/vbatts/git-validation/rules/dco"
|
||||||
_ "github.com/vbatts/git-validation/rules/messageregexp"
|
_ "github.com/vbatts/git-validation/rules/messageregexp"
|
||||||
|
|
@ -22,10 +22,15 @@ var (
|
||||||
flDebug = flag.Bool("D", false, "debug output")
|
flDebug = flag.Bool("D", false, "debug output")
|
||||||
flQuiet = flag.Bool("q", false, "less output")
|
flQuiet = flag.Bool("q", false, "less output")
|
||||||
flDir = flag.String("d", ".", "git directory to validate from")
|
flDir = flag.String("d", ".", "git directory to validate from")
|
||||||
|
flNoGithub = flag.Bool("no-github", false, "disables Github Actions environment checks (when env GITHUB_ACTIONS=true is set)")
|
||||||
flNoTravis = flag.Bool("no-travis", false, "disables travis environment checks (when env TRAVIS=true is set)")
|
flNoTravis = flag.Bool("no-travis", false, "disables travis environment checks (when env TRAVIS=true is set)")
|
||||||
flTravisPROnly = flag.Bool("travis-pr-only", true, "when on travis, only run validations if the CI-Build is checking pull-request build")
|
flTravisPROnly = flag.Bool("travis-pr-only", true, "when on travis, only run validations if the CI-Build is checking pull-request build")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logrus.SetOutput(os.Stderr)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
|
@ -61,7 +66,7 @@ func main() {
|
||||||
rules = validate.FilterRules(validate.RegisteredRules, validate.SanitizeFilters(*flRun))
|
rules = validate.FilterRules(validate.RegisteredRules, validate.SanitizeFilters(*flRun))
|
||||||
}
|
}
|
||||||
if os.Getenv("DEBUG") != "" {
|
if os.Getenv("DEBUG") != "" {
|
||||||
log.Printf("%#v", rules) // XXX maybe reduce this list
|
logrus.Printf("%#v", rules) // XXX maybe reduce this list
|
||||||
}
|
}
|
||||||
|
|
||||||
var commitRange = *flCommitRange
|
var commitRange = *flCommitRange
|
||||||
|
|
@ -73,15 +78,20 @@ func main() {
|
||||||
commitRange = os.Getenv("TRAVIS_COMMIT")
|
commitRange = os.Getenv("TRAVIS_COMMIT")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// https://docs.github.com/en/actions/reference/environment-variables
|
||||||
|
if strings.ToLower(os.Getenv("GITHUB_ACTIONS")) == "true" && !*flNoGithub {
|
||||||
|
commitRange = fmt.Sprintf("%s..%s", os.Getenv("GITHUB_SHA"), "HEAD")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Infof("using commit range: %s", commitRange)
|
||||||
runner, err := validate.NewRunner(*flDir, rules, commitRange, *flVerbose)
|
runner, err := validate.NewRunner(*flDir, rules, commitRange, *flVerbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := runner.Run(); err != nil {
|
if err := runner.Run(); err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
_, fail := runner.Results.PassFail()
|
_, fail := runner.Results.PassFail()
|
||||||
if fail > 0 {
|
if fail > 0 {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@ func SanitizeFilters(filtStr string) (filters []string) {
|
||||||
//
|
//
|
||||||
// Some `includes` rules have values assigned to them.
|
// Some `includes` rules have values assigned to them.
|
||||||
// i.e. -run "dco,message_regexp='^JIRA-[0-9]+ [A-Z].*$'"
|
// i.e. -run "dco,message_regexp='^JIRA-[0-9]+ [A-Z].*$'"
|
||||||
//
|
|
||||||
func FilterRules(rules []Rule, includes []string) []Rule {
|
func FilterRules(rules []Rule, includes []string) []Rule {
|
||||||
ret := []Rule{}
|
ret := []Rule{}
|
||||||
|
|
||||||
|
|
@ -83,7 +82,7 @@ func FilterRules(rules []Rule, includes []string) []Rule {
|
||||||
for i := range includes {
|
for i := range includes {
|
||||||
if strings.Contains(includes[i], "=") {
|
if strings.Contains(includes[i], "=") {
|
||||||
chunks := strings.SplitN(includes[i], "=", 2)
|
chunks := strings.SplitN(includes[i], "=", 2)
|
||||||
if strings.ToLower(r.Name) == strings.ToLower(chunks[0]) {
|
if strings.EqualFold(r.Name, chunks[0]) {
|
||||||
// for these rules, the Name won't be unique per se. There may be
|
// for these rules, the Name won't be unique per se. There may be
|
||||||
// multiple "regexp=" with different values. We'll need to set the
|
// multiple "regexp=" with different values. We'll need to set the
|
||||||
// .Value = chunk[1] and ensure r is dup'ed so they don't clobber
|
// .Value = chunk[1] and ensure r is dup'ed so they don't clobber
|
||||||
|
|
@ -93,7 +92,7 @@ func FilterRules(rules []Rule, includes []string) []Rule {
|
||||||
ret = append(ret, newR)
|
ret = append(ret, newR)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if strings.ToLower(r.Name) == strings.ToLower(includes[i]) {
|
if strings.EqualFold(r.Name, includes[i]) {
|
||||||
ret = append(ret, r)
|
ret = append(ret, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/vbatts/git-validation/git"
|
"github.com/vbatts/git-validation/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,7 +30,11 @@ func NewRunner(root string, rules []Rule, commitrange string, verbose bool) (*Ru
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer os.Chdir(cwd)
|
defer func() {
|
||||||
|
if err := os.Chdir(cwd); err != nil {
|
||||||
|
logrus.Warnf("changing working directory to %q failed: %s", cwd, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err := os.Chdir(newroot); err != nil {
|
if err := os.Chdir(newroot); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -42,6 +47,7 @@ func NewRunner(root string, rules []Rule, commitrange string, verbose bool) (*Ru
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logrus.Debugf("[NewRunner] commitrange: %q", commitrange)
|
||||||
return &Runner{
|
return &Runner{
|
||||||
Root: newroot,
|
Root: newroot,
|
||||||
Rules: rules,
|
Rules: rules,
|
||||||
|
|
@ -56,7 +62,11 @@ func (r *Runner) Run() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer os.Chdir(cwd)
|
defer func() {
|
||||||
|
if err := os.Chdir(cwd); err != nil {
|
||||||
|
logrus.Warnf("changing working directory to %q failed: %s", cwd, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if err := os.Chdir(r.Root); err != nil {
|
if err := os.Chdir(r.Root); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
Copyright 2009 The Go Authors.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|
@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
in the documentation and/or other materials provided with the
|
in the documentation and/or other materials provided with the
|
||||||
distribution.
|
distribution.
|
||||||
* Neither the name of Google Inc. nor the names of its
|
* Neither the name of Google LLC nor the names of its
|
||||||
contributors may be used to endorse or promote products derived from
|
contributors may be used to endorse or promote products derived from
|
||||||
this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ from the generated architecture-specific files listed below, and merge these
|
||||||
into a common file for each OS.
|
into a common file for each OS.
|
||||||
|
|
||||||
The merge is performed in the following steps:
|
The merge is performed in the following steps:
|
||||||
1. Construct the set of common code that is idential in all architecture-specific files.
|
1. Construct the set of common code that is identical in all architecture-specific files.
|
||||||
2. Write this common code to the merged file.
|
2. Write this common code to the merged file.
|
||||||
3. Remove the common code from all architecture-specific files.
|
3. Remove the common code from all architecture-specific files.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (freebsd || netbsd || openbsd) && gc
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
// +build freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (freebsd || netbsd || openbsd) && gc
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
// +build freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
// +build darwin freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
// +build darwin freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
// +build darwin freebsd netbsd openbsd
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && arm64 && gc
|
//go:build linux && arm64 && gc
|
||||||
// +build linux
|
|
||||||
// +build arm64
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && loong64 && gc
|
//go:build linux && loong64 && gc
|
||||||
// +build linux
|
|
||||||
// +build loong64
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && (mips64 || mips64le) && gc
|
//go:build linux && (mips64 || mips64le) && gc
|
||||||
// +build linux
|
|
||||||
// +build mips64 mips64le
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && (mips || mipsle) && gc
|
//go:build linux && (mips || mipsle) && gc
|
||||||
// +build linux
|
|
||||||
// +build mips mipsle
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && (ppc64 || ppc64le) && gc
|
//go:build linux && (ppc64 || ppc64le) && gc
|
||||||
// +build linux
|
|
||||||
// +build ppc64 ppc64le
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build riscv64 && gc
|
//go:build riscv64 && gc
|
||||||
// +build riscv64
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build linux && s390x && gc
|
//go:build linux && s390x && gc
|
||||||
// +build linux
|
|
||||||
// +build s390x
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc
|
//go:build gc
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build zos && s390x && gc
|
//go:build zos && s390x && gc
|
||||||
// +build zos
|
|
||||||
// +build s390x
|
|
||||||
// +build gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
#define PSALAA 1208(R0)
|
#define PSALAA 1208(R0)
|
||||||
#define GTAB64(x) 80(x)
|
#define GTAB64(x) 80(x)
|
||||||
#define LCA64(x) 88(x)
|
#define LCA64(x) 88(x)
|
||||||
|
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
|
||||||
#define CAA(x) 8(x)
|
#define CAA(x) 8(x)
|
||||||
#define EDCHPXV(x) 1016(x) // in the CAA
|
#define CEECAATHDID(x) 976(x) // in the CAA
|
||||||
#define SAVSTACK_ASYNC(x) 336(x) // in the LCA
|
#define EDCHPXV(x) 1016(x) // in the CAA
|
||||||
|
#define GOCB(x) 1104(x) // in the CAA
|
||||||
|
|
||||||
// SS_*, where x=SAVSTACK_ASYNC
|
// SS_*, where x=SAVSTACK_ASYNC
|
||||||
#define SS_LE(x) 0(x)
|
#define SS_LE(x) 0(x)
|
||||||
|
|
@ -22,394 +21,125 @@
|
||||||
#define SS_ERRNO(x) 16(x)
|
#define SS_ERRNO(x) 16(x)
|
||||||
#define SS_ERRNOJR(x) 20(x)
|
#define SS_ERRNOJR(x) 20(x)
|
||||||
|
|
||||||
#define LE_CALL BYTE $0x0D; BYTE $0x76; // BL R7, R6
|
// Function Descriptor Offsets
|
||||||
|
#define __errno 0x156*16
|
||||||
|
#define __err2ad 0x16C*16
|
||||||
|
|
||||||
TEXT ·clearErrno(SB),NOSPLIT,$0-0
|
// Call Instructions
|
||||||
BL addrerrno<>(SB)
|
#define LE_CALL BYTE $0x0D; BYTE $0x76 // BL R7, R6
|
||||||
MOVD $0, 0(R3)
|
#define SVC_LOAD BYTE $0x0A; BYTE $0x08 // SVC 08 LOAD
|
||||||
|
#define SVC_DELETE BYTE $0x0A; BYTE $0x09 // SVC 09 DELETE
|
||||||
|
|
||||||
|
DATA zosLibVec<>(SB)/8, $0
|
||||||
|
GLOBL zosLibVec<>(SB), NOPTR, $8
|
||||||
|
|
||||||
|
TEXT ·initZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
MOVD CAA(R8), R8
|
||||||
|
MOVD EDCHPXV(R8), R8
|
||||||
|
MOVD R8, zosLibVec<>(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·GetZosLibVec(SB), NOSPLIT|NOFRAME, $0-0
|
||||||
|
MOVD zosLibVec<>(SB), R8
|
||||||
|
MOVD R8, ret+0(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·clearErrno(SB), NOSPLIT, $0-0
|
||||||
|
BL addrerrno<>(SB)
|
||||||
|
MOVD $0, 0(R3)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// Returns the address of errno in R3.
|
// Returns the address of errno in R3.
|
||||||
TEXT addrerrno<>(SB),NOSPLIT|NOFRAME,$0-0
|
TEXT addrerrno<>(SB), NOSPLIT|NOFRAME, $0-0
|
||||||
// Get library control area (LCA).
|
// Get library control area (LCA).
|
||||||
MOVW PSALAA, R8
|
MOVW PSALAA, R8
|
||||||
MOVD LCA64(R8), R8
|
MOVD LCA64(R8), R8
|
||||||
|
|
||||||
// Get __errno FuncDesc.
|
// Get __errno FuncDesc.
|
||||||
MOVD CAA(R8), R9
|
MOVD CAA(R8), R9
|
||||||
MOVD EDCHPXV(R9), R9
|
MOVD EDCHPXV(R9), R9
|
||||||
ADD $(0x156*16), R9
|
ADD $(__errno), R9
|
||||||
LMG 0(R9), R5, R6
|
LMG 0(R9), R5, R6
|
||||||
|
|
||||||
// Switch to saved LE stack.
|
// Switch to saved LE stack.
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
MOVD 0(R9), R4
|
MOVD 0(R9), R4
|
||||||
MOVD $0, 0(R9)
|
MOVD $0, 0(R9)
|
||||||
|
|
||||||
// Call __errno function.
|
// Call __errno function.
|
||||||
LE_CALL
|
LE_CALL
|
||||||
NOPH
|
NOPH
|
||||||
|
|
||||||
// Switch back to Go stack.
|
// Switch back to Go stack.
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
XOR R0, R0 // Restore R0 to $0.
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_syscall(SB),NOSPLIT,$0-56
|
|
||||||
BL runtime·entersyscall(SB)
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+32(FP)
|
|
||||||
MOVD R0, r2+40(FP)
|
|
||||||
MOVD R0, err+48(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL addrerrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+48(FP)
|
|
||||||
done:
|
|
||||||
BL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_rawsyscall(SB),NOSPLIT,$0-56
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+32(FP)
|
|
||||||
MOVD R0, r2+40(FP)
|
|
||||||
MOVD R0, err+48(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL addrerrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+48(FP)
|
|
||||||
done:
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_syscall6(SB),NOSPLIT,$0-80
|
|
||||||
BL runtime·entersyscall(SB)
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Fill in parameter list.
|
|
||||||
MOVD a4+32(FP), R12
|
|
||||||
MOVD R12, (2176+24)(R4)
|
|
||||||
MOVD a5+40(FP), R12
|
|
||||||
MOVD R12, (2176+32)(R4)
|
|
||||||
MOVD a6+48(FP), R12
|
|
||||||
MOVD R12, (2176+40)(R4)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+56(FP)
|
|
||||||
MOVD R0, r2+64(FP)
|
|
||||||
MOVD R0, err+72(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL addrerrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+72(FP)
|
|
||||||
done:
|
|
||||||
BL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_rawsyscall6(SB),NOSPLIT,$0-80
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Fill in parameter list.
|
|
||||||
MOVD a4+32(FP), R12
|
|
||||||
MOVD R12, (2176+24)(R4)
|
|
||||||
MOVD a5+40(FP), R12
|
|
||||||
MOVD R12, (2176+32)(R4)
|
|
||||||
MOVD a6+48(FP), R12
|
|
||||||
MOVD R12, (2176+40)(R4)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+56(FP)
|
|
||||||
MOVD R0, r2+64(FP)
|
|
||||||
MOVD R0, err+72(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL ·rrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+72(FP)
|
|
||||||
done:
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_syscall9(SB),NOSPLIT,$0
|
|
||||||
BL runtime·entersyscall(SB)
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Fill in parameter list.
|
|
||||||
MOVD a4+32(FP), R12
|
|
||||||
MOVD R12, (2176+24)(R4)
|
|
||||||
MOVD a5+40(FP), R12
|
|
||||||
MOVD R12, (2176+32)(R4)
|
|
||||||
MOVD a6+48(FP), R12
|
|
||||||
MOVD R12, (2176+40)(R4)
|
|
||||||
MOVD a7+56(FP), R12
|
|
||||||
MOVD R12, (2176+48)(R4)
|
|
||||||
MOVD a8+64(FP), R12
|
|
||||||
MOVD R12, (2176+56)(R4)
|
|
||||||
MOVD a9+72(FP), R12
|
|
||||||
MOVD R12, (2176+64)(R4)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+80(FP)
|
|
||||||
MOVD R0, r2+88(FP)
|
|
||||||
MOVD R0, err+96(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL addrerrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+96(FP)
|
|
||||||
done:
|
|
||||||
BL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·syscall_rawsyscall9(SB),NOSPLIT,$0
|
|
||||||
MOVD a1+8(FP), R1
|
|
||||||
MOVD a2+16(FP), R2
|
|
||||||
MOVD a3+24(FP), R3
|
|
||||||
|
|
||||||
// Get library control area (LCA).
|
|
||||||
MOVW PSALAA, R8
|
|
||||||
MOVD LCA64(R8), R8
|
|
||||||
|
|
||||||
// Get function.
|
|
||||||
MOVD CAA(R8), R9
|
|
||||||
MOVD EDCHPXV(R9), R9
|
|
||||||
MOVD trap+0(FP), R5
|
|
||||||
SLD $4, R5
|
|
||||||
ADD R5, R9
|
|
||||||
LMG 0(R9), R5, R6
|
|
||||||
|
|
||||||
// Restore LE stack.
|
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
|
||||||
MOVD 0(R9), R4
|
|
||||||
MOVD $0, 0(R9)
|
|
||||||
|
|
||||||
// Fill in parameter list.
|
|
||||||
MOVD a4+32(FP), R12
|
|
||||||
MOVD R12, (2176+24)(R4)
|
|
||||||
MOVD a5+40(FP), R12
|
|
||||||
MOVD R12, (2176+32)(R4)
|
|
||||||
MOVD a6+48(FP), R12
|
|
||||||
MOVD R12, (2176+40)(R4)
|
|
||||||
MOVD a7+56(FP), R12
|
|
||||||
MOVD R12, (2176+48)(R4)
|
|
||||||
MOVD a8+64(FP), R12
|
|
||||||
MOVD R12, (2176+56)(R4)
|
|
||||||
MOVD a9+72(FP), R12
|
|
||||||
MOVD R12, (2176+64)(R4)
|
|
||||||
|
|
||||||
// Call function.
|
|
||||||
LE_CALL
|
|
||||||
NOPH
|
|
||||||
XOR R0, R0 // Restore R0 to $0.
|
|
||||||
MOVD R4, 0(R9) // Save stack pointer.
|
|
||||||
|
|
||||||
MOVD R3, r1+80(FP)
|
|
||||||
MOVD R0, r2+88(FP)
|
|
||||||
MOVD R0, err+96(FP)
|
|
||||||
MOVW R3, R4
|
|
||||||
CMP R4, $-1
|
|
||||||
BNE done
|
|
||||||
BL addrerrno<>(SB)
|
|
||||||
MOVWZ 0(R3), R3
|
|
||||||
MOVD R3, err+96(FP)
|
|
||||||
done:
|
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64)
|
// func svcCall(fnptr unsafe.Pointer, argv *unsafe.Pointer, dsa *uint64)
|
||||||
TEXT ·svcCall(SB),NOSPLIT,$0
|
TEXT ·svcCall(SB), NOSPLIT, $0
|
||||||
BL runtime·save_g(SB) // Save g and stack pointer
|
BL runtime·save_g(SB) // Save g and stack pointer
|
||||||
MOVW PSALAA, R8
|
MOVW PSALAA, R8
|
||||||
MOVD LCA64(R8), R8
|
MOVD LCA64(R8), R8
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
MOVD R15, 0(R9)
|
MOVD R15, 0(R9)
|
||||||
|
|
||||||
MOVD argv+8(FP), R1 // Move function arguments into registers
|
MOVD argv+8(FP), R1 // Move function arguments into registers
|
||||||
MOVD dsa+16(FP), g
|
MOVD dsa+16(FP), g
|
||||||
MOVD fnptr+0(FP), R15
|
MOVD fnptr+0(FP), R15
|
||||||
|
|
||||||
BYTE $0x0D // Branch to function
|
BYTE $0x0D // Branch to function
|
||||||
BYTE $0xEF
|
BYTE $0xEF
|
||||||
|
|
||||||
BL runtime·load_g(SB) // Restore g and stack pointer
|
BL runtime·load_g(SB) // Restore g and stack pointer
|
||||||
MOVW PSALAA, R8
|
MOVW PSALAA, R8
|
||||||
MOVD LCA64(R8), R8
|
MOVD LCA64(R8), R8
|
||||||
MOVD SAVSTACK_ASYNC(R8), R9
|
MOVD SAVSTACK_ASYNC(R8), R9
|
||||||
MOVD 0(R9), R15
|
MOVD 0(R9), R15
|
||||||
|
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func svcLoad(name *byte) unsafe.Pointer
|
// func svcLoad(name *byte) unsafe.Pointer
|
||||||
TEXT ·svcLoad(SB),NOSPLIT,$0
|
TEXT ·svcLoad(SB), NOSPLIT, $0
|
||||||
MOVD R15, R2 // Save go stack pointer
|
MOVD R15, R2 // Save go stack pointer
|
||||||
MOVD name+0(FP), R0 // Move SVC args into registers
|
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||||
MOVD $0x80000000, R1
|
MOVD $0x80000000, R1
|
||||||
MOVD $0, R15
|
MOVD $0, R15
|
||||||
BYTE $0x0A // SVC 08 LOAD
|
SVC_LOAD
|
||||||
BYTE $0x08
|
MOVW R15, R3 // Save return code from SVC
|
||||||
MOVW R15, R3 // Save return code from SVC
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
MOVD R2, R15 // Restore go stack pointer
|
CMP R3, $0 // Check SVC return code
|
||||||
CMP R3, $0 // Check SVC return code
|
BNE error
|
||||||
BNE error
|
|
||||||
|
|
||||||
MOVD $-2, R3 // Reset last bit of entry point to zero
|
MOVD $-2, R3 // Reset last bit of entry point to zero
|
||||||
AND R0, R3
|
AND R0, R3
|
||||||
MOVD R3, addr+8(FP) // Return entry point returned by SVC
|
MOVD R3, ret+8(FP) // Return entry point returned by SVC
|
||||||
CMP R0, R3 // Check if last bit of entry point was set
|
CMP R0, R3 // Check if last bit of entry point was set
|
||||||
BNE done
|
BNE done
|
||||||
|
|
||||||
MOVD R15, R2 // Save go stack pointer
|
MOVD R15, R2 // Save go stack pointer
|
||||||
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
|
MOVD $0, R15 // Move SVC args into registers (entry point still in r0 from SVC 08)
|
||||||
BYTE $0x0A // SVC 09 DELETE
|
SVC_DELETE
|
||||||
BYTE $0x09
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
MOVD R2, R15 // Restore go stack pointer
|
|
||||||
|
|
||||||
error:
|
error:
|
||||||
MOVD $0, addr+8(FP) // Return 0 on failure
|
MOVD $0, ret+8(FP) // Return 0 on failure
|
||||||
|
|
||||||
done:
|
done:
|
||||||
XOR R0, R0 // Reset r0 to 0
|
XOR R0, R0 // Reset r0 to 0
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64
|
// func svcUnload(name *byte, fnptr unsafe.Pointer) int64
|
||||||
TEXT ·svcUnload(SB),NOSPLIT,$0
|
TEXT ·svcUnload(SB), NOSPLIT, $0
|
||||||
MOVD R15, R2 // Save go stack pointer
|
MOVD R15, R2 // Save go stack pointer
|
||||||
MOVD name+0(FP), R0 // Move SVC args into registers
|
MOVD name+0(FP), R0 // Move SVC args into registers
|
||||||
MOVD addr+8(FP), R15
|
MOVD fnptr+8(FP), R15
|
||||||
BYTE $0x0A // SVC 09
|
SVC_DELETE
|
||||||
BYTE $0x09
|
XOR R0, R0 // Reset r0 to 0
|
||||||
XOR R0, R0 // Reset r0 to 0
|
MOVD R15, R1 // Save SVC return code
|
||||||
MOVD R15, R1 // Save SVC return code
|
MOVD R2, R15 // Restore go stack pointer
|
||||||
MOVD R2, R15 // Restore go stack pointer
|
MOVD R1, ret+16(FP) // Return SVC return code
|
||||||
MOVD R1, rc+0(FP) // Return SVC return code
|
|
||||||
RET
|
RET
|
||||||
|
|
||||||
// func gettid() uint64
|
// func gettid() uint64
|
||||||
|
|
@ -420,7 +150,233 @@ TEXT ·gettid(SB), NOSPLIT, $0
|
||||||
|
|
||||||
// Get CEECAATHDID
|
// Get CEECAATHDID
|
||||||
MOVD CAA(R8), R9
|
MOVD CAA(R8), R9
|
||||||
MOVD 0x3D0(R9), R9
|
MOVD CEECAATHDID(R9), R9
|
||||||
MOVD R9, ret+0(FP)
|
MOVD R9, ret+0(FP)
|
||||||
|
|
||||||
RET
|
RET
|
||||||
|
|
||||||
|
//
|
||||||
|
// Call LE function, if the return is -1
|
||||||
|
// errno and errno2 is retrieved
|
||||||
|
//
|
||||||
|
TEXT ·CallLeFuncWithErr(SB), NOSPLIT, $0
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD g, GOCB(R9)
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
|
||||||
|
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
|
||||||
|
|
||||||
|
MOVD parms_base+8(FP), R7 // R7 -> argument array
|
||||||
|
MOVD parms_len+16(FP), R8 // R8 number of arguments
|
||||||
|
|
||||||
|
// arg 1 ---> R1
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
MOVD 0(R7), R1
|
||||||
|
|
||||||
|
// arg 2 ---> R2
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R2
|
||||||
|
|
||||||
|
// arg 3 --> R3
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R3
|
||||||
|
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
|
||||||
|
|
||||||
|
repeat:
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R0 // advance arg pointer by 8 byte
|
||||||
|
ADD $8, R6 // advance LE argument address by 8 byte
|
||||||
|
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
|
||||||
|
SUB $1, R8
|
||||||
|
CMP R8, $0
|
||||||
|
BNE repeat
|
||||||
|
|
||||||
|
docall:
|
||||||
|
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
|
||||||
|
LE_CALL // balr R7, R6 (return #1)
|
||||||
|
NOPH
|
||||||
|
MOVD R3, ret+32(FP)
|
||||||
|
CMP R3, $-1 // compare result to -1
|
||||||
|
BNE done
|
||||||
|
|
||||||
|
// retrieve errno and errno2
|
||||||
|
MOVD zosLibVec<>(SB), R8
|
||||||
|
ADD $(__errno), R8
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
LE_CALL // balr R7, R6 __errno (return #3)
|
||||||
|
NOPH
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+48(FP)
|
||||||
|
MOVD zosLibVec<>(SB), R8
|
||||||
|
ADD $(__err2ad), R8
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
LE_CALL // balr R7, R6 __err2ad (return #2)
|
||||||
|
NOPH
|
||||||
|
MOVW (R3), R2 // retrieve errno2
|
||||||
|
MOVD R2, errno2+40(FP) // store in return area
|
||||||
|
|
||||||
|
done:
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
RET
|
||||||
|
|
||||||
|
//
|
||||||
|
// Call LE function, if the return is 0
|
||||||
|
// errno and errno2 is retrieved
|
||||||
|
//
|
||||||
|
TEXT ·CallLeFuncWithPtrReturn(SB), NOSPLIT, $0
|
||||||
|
MOVW PSALAA, R8
|
||||||
|
MOVD LCA64(R8), R8
|
||||||
|
MOVD CAA(R8), R9
|
||||||
|
MOVD g, GOCB(R9)
|
||||||
|
|
||||||
|
// Restore LE stack.
|
||||||
|
MOVD SAVSTACK_ASYNC(R8), R9 // R9-> LE stack frame saving address
|
||||||
|
MOVD 0(R9), R4 // R4-> restore previously saved stack frame pointer
|
||||||
|
|
||||||
|
MOVD parms_base+8(FP), R7 // R7 -> argument array
|
||||||
|
MOVD parms_len+16(FP), R8 // R8 number of arguments
|
||||||
|
|
||||||
|
// arg 1 ---> R1
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
MOVD 0(R7), R1
|
||||||
|
|
||||||
|
// arg 2 ---> R2
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R2
|
||||||
|
|
||||||
|
// arg 3 --> R3
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
SUB $1, R8
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R3
|
||||||
|
|
||||||
|
CMP R8, $0
|
||||||
|
BEQ docall
|
||||||
|
MOVD $2176+16, R6 // starting LE stack address-8 to store 4th argument
|
||||||
|
|
||||||
|
repeat:
|
||||||
|
ADD $8, R7
|
||||||
|
MOVD 0(R7), R0 // advance arg pointer by 8 byte
|
||||||
|
ADD $8, R6 // advance LE argument address by 8 byte
|
||||||
|
MOVD R0, (R4)(R6*1) // copy argument from go-slice to le-frame
|
||||||
|
SUB $1, R8
|
||||||
|
CMP R8, $0
|
||||||
|
BNE repeat
|
||||||
|
|
||||||
|
docall:
|
||||||
|
MOVD funcdesc+0(FP), R8 // R8-> function descriptor
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
MOVD $0, 0(R9) // R9 address of SAVSTACK_ASYNC
|
||||||
|
LE_CALL // balr R7, R6 (return #1)
|
||||||
|
NOPH
|
||||||
|
MOVD R3, ret+32(FP)
|
||||||
|
CMP R3, $0 // compare result to 0
|
||||||
|
BNE done
|
||||||
|
|
||||||
|
// retrieve errno and errno2
|
||||||
|
MOVD zosLibVec<>(SB), R8
|
||||||
|
ADD $(__errno), R8
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
LE_CALL // balr R7, R6 __errno (return #3)
|
||||||
|
NOPH
|
||||||
|
MOVWZ 0(R3), R3
|
||||||
|
MOVD R3, err+48(FP)
|
||||||
|
MOVD zosLibVec<>(SB), R8
|
||||||
|
ADD $(__err2ad), R8
|
||||||
|
LMG 0(R8), R5, R6
|
||||||
|
LE_CALL // balr R7, R6 __err2ad (return #2)
|
||||||
|
NOPH
|
||||||
|
MOVW (R3), R2 // retrieve errno2
|
||||||
|
MOVD R2, errno2+40(FP) // store in return area
|
||||||
|
XOR R2, R2
|
||||||
|
MOVWZ R2, (R3) // clear errno2
|
||||||
|
|
||||||
|
done:
|
||||||
|
MOVD R4, 0(R9) // Save stack pointer.
|
||||||
|
RET
|
||||||
|
|
||||||
|
//
|
||||||
|
// function to test if a pointer can be safely dereferenced (content read)
|
||||||
|
// return 0 for succces
|
||||||
|
//
|
||||||
|
TEXT ·ptrtest(SB), NOSPLIT, $0-16
|
||||||
|
MOVD arg+0(FP), R10 // test pointer in R10
|
||||||
|
|
||||||
|
// set up R2 to point to CEECAADMC
|
||||||
|
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
|
||||||
|
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
|
||||||
|
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
|
||||||
|
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
|
||||||
|
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
|
||||||
|
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
|
||||||
|
|
||||||
|
// set up R5 to point to the "shunt" path which set 1 to R3 (failure)
|
||||||
|
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
|
||||||
|
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
|
||||||
|
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
|
||||||
|
|
||||||
|
// if r3 is not zero (failed) then branch to finish
|
||||||
|
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
|
||||||
|
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
|
||||||
|
|
||||||
|
// stomic store shunt address in R5 into CEECAADMC
|
||||||
|
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
|
||||||
|
|
||||||
|
// now try reading from the test pointer in R10, if it fails it branches to the "lghi" instruction above
|
||||||
|
BYTE $0xE3; BYTE $0x9A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 9,0(10)
|
||||||
|
|
||||||
|
// finish here, restore 0 into CEECAADMC
|
||||||
|
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
|
||||||
|
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
|
||||||
|
MOVD R3, ret+8(FP) // result in R3
|
||||||
|
RET
|
||||||
|
|
||||||
|
//
|
||||||
|
// function to test if a untptr can be loaded from a pointer
|
||||||
|
// return 1: the 8-byte content
|
||||||
|
// 2: 0 for success, 1 for failure
|
||||||
|
//
|
||||||
|
// func safeload(ptr uintptr) ( value uintptr, error uintptr)
|
||||||
|
TEXT ·safeload(SB), NOSPLIT, $0-24
|
||||||
|
MOVD ptr+0(FP), R10 // test pointer in R10
|
||||||
|
MOVD $0x0, R6
|
||||||
|
BYTE $0xE3; BYTE $0x20; BYTE $0x04; BYTE $0xB8; BYTE $0x00; BYTE $0x17 // llgt 2,1208
|
||||||
|
BYTE $0xB9; BYTE $0x17; BYTE $0x00; BYTE $0x22 // llgtr 2,2
|
||||||
|
BYTE $0xA5; BYTE $0x26; BYTE $0x7F; BYTE $0xFF // nilh 2,32767
|
||||||
|
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x58; BYTE $0x00; BYTE $0x04 // lg 2,88(2)
|
||||||
|
BYTE $0xE3; BYTE $0x22; BYTE $0x00; BYTE $0x08; BYTE $0x00; BYTE $0x04 // lg 2,8(2)
|
||||||
|
BYTE $0x41; BYTE $0x22; BYTE $0x03; BYTE $0x68 // la 2,872(2)
|
||||||
|
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x33 // xgr 3,3
|
||||||
|
BYTE $0xA7; BYTE $0x55; BYTE $0x00; BYTE $0x04 // bras 5,lbl1
|
||||||
|
BYTE $0xA7; BYTE $0x39; BYTE $0x00; BYTE $0x01 // lghi 3,1
|
||||||
|
BYTE $0xB9; BYTE $0x02; BYTE $0x00; BYTE $0x33 // lbl1 ltgr 3,3
|
||||||
|
BYTE $0xA7; BYTE $0x74; BYTE $0x00; BYTE $0x08 // brc b'0111',lbl2
|
||||||
|
BYTE $0xE3; BYTE $0x52; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 5,0(2)
|
||||||
|
BYTE $0xE3; BYTE $0x6A; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x04 // lg 6,0(10)
|
||||||
|
BYTE $0xB9; BYTE $0x82; BYTE $0x00; BYTE $0x99 // lbl2 xgr 9,9
|
||||||
|
BYTE $0xE3; BYTE $0x92; BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x24 // stg 9,0(2)
|
||||||
|
MOVD R6, value+8(FP) // result in R6
|
||||||
|
MOVD R3, error+16(FP) // error in R3
|
||||||
|
RET
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2025 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.
|
||||||
|
|
||||||
|
//go:build go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos)
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname runtime_getAuxv runtime.getAuxv
|
||||||
|
func runtime_getAuxv() []uintptr
|
||||||
|
|
||||||
|
// Auxv returns the ELF auxiliary vector as a sequence of key/value pairs.
|
||||||
|
// The returned slice is always a fresh copy, owned by the caller.
|
||||||
|
// It returns an error on non-ELF platforms, or if the auxiliary vector cannot be accessed,
|
||||||
|
// which happens in some locked-down environments and build modes.
|
||||||
|
func Auxv() ([][2]uintptr, error) {
|
||||||
|
vec := runtime_getAuxv()
|
||||||
|
vecLen := len(vec)
|
||||||
|
|
||||||
|
if vecLen == 0 {
|
||||||
|
return nil, syscall.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
if vecLen%2 != 0 {
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]uintptr, vecLen)
|
||||||
|
copy(result, vec)
|
||||||
|
return unsafe.Slice((*[2]uintptr)(unsafe.Pointer(&result[0])), vecLen/2), nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2025 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.
|
||||||
|
|
||||||
|
//go:build !go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos)
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func Auxv() ([][2]uintptr, error) {
|
||||||
|
return nil, syscall.ENOTSUP
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,657 @@
|
||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
//go:build zos
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func A2e([]byte)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func E2a([]byte)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BPX4STA = 192 // stat
|
||||||
|
BPX4FST = 104 // fstat
|
||||||
|
BPX4LST = 132 // lstat
|
||||||
|
BPX4OPN = 156 // open
|
||||||
|
BPX4CLO = 72 // close
|
||||||
|
BPX4CHR = 500 // chattr
|
||||||
|
BPX4FCR = 504 // fchattr
|
||||||
|
BPX4LCR = 1180 // lchattr
|
||||||
|
BPX4CTW = 492 // cond_timed_wait
|
||||||
|
BPX4GTH = 1056 // __getthent
|
||||||
|
BPX4PTQ = 412 // pthread_quiesc
|
||||||
|
BPX4PTR = 320 // ptrace
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//options
|
||||||
|
//byte1
|
||||||
|
BPX_OPNFHIGH = 0x80
|
||||||
|
//byte2
|
||||||
|
BPX_OPNFEXEC = 0x80
|
||||||
|
//byte3
|
||||||
|
BPX_O_NOLARGEFILE = 0x08
|
||||||
|
BPX_O_LARGEFILE = 0x04
|
||||||
|
BPX_O_ASYNCSIG = 0x02
|
||||||
|
BPX_O_SYNC = 0x01
|
||||||
|
//byte4
|
||||||
|
BPX_O_CREXCL = 0xc0
|
||||||
|
BPX_O_CREAT = 0x80
|
||||||
|
BPX_O_EXCL = 0x40
|
||||||
|
BPX_O_NOCTTY = 0x20
|
||||||
|
BPX_O_TRUNC = 0x10
|
||||||
|
BPX_O_APPEND = 0x08
|
||||||
|
BPX_O_NONBLOCK = 0x04
|
||||||
|
BPX_FNDELAY = 0x04
|
||||||
|
BPX_O_RDWR = 0x03
|
||||||
|
BPX_O_RDONLY = 0x02
|
||||||
|
BPX_O_WRONLY = 0x01
|
||||||
|
BPX_O_ACCMODE = 0x03
|
||||||
|
BPX_O_GETFL = 0x0f
|
||||||
|
|
||||||
|
//mode
|
||||||
|
// byte1 (file type)
|
||||||
|
BPX_FT_DIR = 1
|
||||||
|
BPX_FT_CHARSPEC = 2
|
||||||
|
BPX_FT_REGFILE = 3
|
||||||
|
BPX_FT_FIFO = 4
|
||||||
|
BPX_FT_SYMLINK = 5
|
||||||
|
BPX_FT_SOCKET = 6
|
||||||
|
//byte3
|
||||||
|
BPX_S_ISUID = 0x08
|
||||||
|
BPX_S_ISGID = 0x04
|
||||||
|
BPX_S_ISVTX = 0x02
|
||||||
|
BPX_S_IRWXU1 = 0x01
|
||||||
|
BPX_S_IRUSR = 0x01
|
||||||
|
//byte4
|
||||||
|
BPX_S_IRWXU2 = 0xc0
|
||||||
|
BPX_S_IWUSR = 0x80
|
||||||
|
BPX_S_IXUSR = 0x40
|
||||||
|
BPX_S_IRWXG = 0x38
|
||||||
|
BPX_S_IRGRP = 0x20
|
||||||
|
BPX_S_IWGRP = 0x10
|
||||||
|
BPX_S_IXGRP = 0x08
|
||||||
|
BPX_S_IRWXOX = 0x07
|
||||||
|
BPX_S_IROTH = 0x04
|
||||||
|
BPX_S_IWOTH = 0x02
|
||||||
|
BPX_S_IXOTH = 0x01
|
||||||
|
|
||||||
|
CW_INTRPT = 1
|
||||||
|
CW_CONDVAR = 32
|
||||||
|
CW_TIMEOUT = 64
|
||||||
|
|
||||||
|
PGTHA_NEXT = 2
|
||||||
|
PGTHA_CURRENT = 1
|
||||||
|
PGTHA_FIRST = 0
|
||||||
|
PGTHA_LAST = 3
|
||||||
|
PGTHA_PROCESS = 0x80
|
||||||
|
PGTHA_CONTTY = 0x40
|
||||||
|
PGTHA_PATH = 0x20
|
||||||
|
PGTHA_COMMAND = 0x10
|
||||||
|
PGTHA_FILEDATA = 0x08
|
||||||
|
PGTHA_THREAD = 0x04
|
||||||
|
PGTHA_PTAG = 0x02
|
||||||
|
PGTHA_COMMANDLONG = 0x01
|
||||||
|
PGTHA_THREADFAST = 0x80
|
||||||
|
PGTHA_FILEPATH = 0x40
|
||||||
|
PGTHA_THDSIGMASK = 0x20
|
||||||
|
// thread quiece mode
|
||||||
|
QUIESCE_TERM int32 = 1
|
||||||
|
QUIESCE_FORCE int32 = 2
|
||||||
|
QUIESCE_QUERY int32 = 3
|
||||||
|
QUIESCE_FREEZE int32 = 4
|
||||||
|
QUIESCE_UNFREEZE int32 = 5
|
||||||
|
FREEZE_THIS_THREAD int32 = 6
|
||||||
|
FREEZE_EXIT int32 = 8
|
||||||
|
QUIESCE_SRB int32 = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pgtha struct {
|
||||||
|
Pid uint32 // 0
|
||||||
|
Tid0 uint32 // 4
|
||||||
|
Tid1 uint32
|
||||||
|
Accesspid byte // C
|
||||||
|
Accesstid byte // D
|
||||||
|
Accessasid uint16 // E
|
||||||
|
Loginname [8]byte // 10
|
||||||
|
Flag1 byte // 18
|
||||||
|
Flag1b2 byte // 19
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bpxystat_t struct { // DSECT BPXYSTAT
|
||||||
|
St_id [4]uint8 // 0
|
||||||
|
St_length uint16 // 0x4
|
||||||
|
St_version uint16 // 0x6
|
||||||
|
St_mode uint32 // 0x8
|
||||||
|
St_ino uint32 // 0xc
|
||||||
|
St_dev uint32 // 0x10
|
||||||
|
St_nlink uint32 // 0x14
|
||||||
|
St_uid uint32 // 0x18
|
||||||
|
St_gid uint32 // 0x1c
|
||||||
|
St_size uint64 // 0x20
|
||||||
|
St_atime uint32 // 0x28
|
||||||
|
St_mtime uint32 // 0x2c
|
||||||
|
St_ctime uint32 // 0x30
|
||||||
|
St_rdev uint32 // 0x34
|
||||||
|
St_auditoraudit uint32 // 0x38
|
||||||
|
St_useraudit uint32 // 0x3c
|
||||||
|
St_blksize uint32 // 0x40
|
||||||
|
St_createtime uint32 // 0x44
|
||||||
|
St_auditid [4]uint32 // 0x48
|
||||||
|
St_res01 uint32 // 0x58
|
||||||
|
Ft_ccsid uint16 // 0x5c
|
||||||
|
Ft_flags uint16 // 0x5e
|
||||||
|
St_res01a [2]uint32 // 0x60
|
||||||
|
St_res02 uint32 // 0x68
|
||||||
|
St_blocks uint32 // 0x6c
|
||||||
|
St_opaque [3]uint8 // 0x70
|
||||||
|
St_visible uint8 // 0x73
|
||||||
|
St_reftime uint32 // 0x74
|
||||||
|
St_fid uint64 // 0x78
|
||||||
|
St_filefmt uint8 // 0x80
|
||||||
|
St_fspflag2 uint8 // 0x81
|
||||||
|
St_res03 [2]uint8 // 0x82
|
||||||
|
St_ctimemsec uint32 // 0x84
|
||||||
|
St_seclabel [8]uint8 // 0x88
|
||||||
|
St_res04 [4]uint8 // 0x90
|
||||||
|
// end of version 1
|
||||||
|
_ uint32 // 0x94
|
||||||
|
St_atime64 uint64 // 0x98
|
||||||
|
St_mtime64 uint64 // 0xa0
|
||||||
|
St_ctime64 uint64 // 0xa8
|
||||||
|
St_createtime64 uint64 // 0xb0
|
||||||
|
St_reftime64 uint64 // 0xb8
|
||||||
|
_ uint64 // 0xc0
|
||||||
|
St_res05 [16]uint8 // 0xc8
|
||||||
|
// end of version 2
|
||||||
|
}
|
||||||
|
|
||||||
|
type BpxFilestatus struct {
|
||||||
|
Oflag1 byte
|
||||||
|
Oflag2 byte
|
||||||
|
Oflag3 byte
|
||||||
|
Oflag4 byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type BpxMode struct {
|
||||||
|
Ftype byte
|
||||||
|
Mode1 byte
|
||||||
|
Mode2 byte
|
||||||
|
Mode3 byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thr attribute structure for extended attributes
|
||||||
|
type Bpxyatt_t struct { // DSECT BPXYATT
|
||||||
|
Att_id [4]uint8
|
||||||
|
Att_version uint16
|
||||||
|
Att_res01 [2]uint8
|
||||||
|
Att_setflags1 uint8
|
||||||
|
Att_setflags2 uint8
|
||||||
|
Att_setflags3 uint8
|
||||||
|
Att_setflags4 uint8
|
||||||
|
Att_mode uint32
|
||||||
|
Att_uid uint32
|
||||||
|
Att_gid uint32
|
||||||
|
Att_opaquemask [3]uint8
|
||||||
|
Att_visblmaskres uint8
|
||||||
|
Att_opaque [3]uint8
|
||||||
|
Att_visibleres uint8
|
||||||
|
Att_size_h uint32
|
||||||
|
Att_size_l uint32
|
||||||
|
Att_atime uint32
|
||||||
|
Att_mtime uint32
|
||||||
|
Att_auditoraudit uint32
|
||||||
|
Att_useraudit uint32
|
||||||
|
Att_ctime uint32
|
||||||
|
Att_reftime uint32
|
||||||
|
// end of version 1
|
||||||
|
Att_filefmt uint8
|
||||||
|
Att_res02 [3]uint8
|
||||||
|
Att_filetag uint32
|
||||||
|
Att_res03 [8]uint8
|
||||||
|
// end of version 2
|
||||||
|
Att_atime64 uint64
|
||||||
|
Att_mtime64 uint64
|
||||||
|
Att_ctime64 uint64
|
||||||
|
Att_reftime64 uint64
|
||||||
|
Att_seclabel [8]uint8
|
||||||
|
Att_ver3res02 [8]uint8
|
||||||
|
// end of version 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxOpen(name string, options *BpxFilestatus, mode *BpxMode) (rv int32, rc int32, rn int32) {
|
||||||
|
if len(name) < 1024 {
|
||||||
|
var namebuf [1024]byte
|
||||||
|
sz := int32(copy(namebuf[:], name))
|
||||||
|
A2e(namebuf[:sz])
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sz)
|
||||||
|
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||||
|
parms[2] = unsafe.Pointer(options)
|
||||||
|
parms[3] = unsafe.Pointer(mode)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4OPN)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxClose(fd int32) (rv int32, rc int32, rn int32) {
|
||||||
|
var parms [4]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&fd)
|
||||||
|
parms[1] = unsafe.Pointer(&rv)
|
||||||
|
parms[2] = unsafe.Pointer(&rc)
|
||||||
|
parms[3] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4CLO)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxFileFStat(fd int32, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||||
|
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||||
|
st.St_version = 2
|
||||||
|
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||||
|
var parms [6]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&fd)
|
||||||
|
parms[1] = unsafe.Pointer(&stat_sz)
|
||||||
|
parms[2] = unsafe.Pointer(st)
|
||||||
|
parms[3] = unsafe.Pointer(&rv)
|
||||||
|
parms[4] = unsafe.Pointer(&rc)
|
||||||
|
parms[5] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4FST)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxFileStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||||
|
if len(name) < 1024 {
|
||||||
|
var namebuf [1024]byte
|
||||||
|
sz := int32(copy(namebuf[:], name))
|
||||||
|
A2e(namebuf[:sz])
|
||||||
|
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||||
|
st.St_version = 2
|
||||||
|
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sz)
|
||||||
|
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||||
|
parms[2] = unsafe.Pointer(&stat_sz)
|
||||||
|
parms[3] = unsafe.Pointer(st)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4STA)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxFileLStat(name string, st *Bpxystat_t) (rv int32, rc int32, rn int32) {
|
||||||
|
if len(name) < 1024 {
|
||||||
|
var namebuf [1024]byte
|
||||||
|
sz := int32(copy(namebuf[:], name))
|
||||||
|
A2e(namebuf[:sz])
|
||||||
|
st.St_id = [4]uint8{0xe2, 0xe3, 0xc1, 0xe3}
|
||||||
|
st.St_version = 2
|
||||||
|
stat_sz := uint32(unsafe.Sizeof(*st))
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sz)
|
||||||
|
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||||
|
parms[2] = unsafe.Pointer(&stat_sz)
|
||||||
|
parms[3] = unsafe.Pointer(st)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4LST)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxChattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||||
|
if len(path) >= 1024 {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
var namebuf [1024]byte
|
||||||
|
sz := int32(copy(namebuf[:], path))
|
||||||
|
A2e(namebuf[:sz])
|
||||||
|
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sz)
|
||||||
|
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||||
|
parms[2] = unsafe.Pointer(&attr_sz)
|
||||||
|
parms[3] = unsafe.Pointer(attr)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4CHR)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxLchattr(path string, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||||
|
if len(path) >= 1024 {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
var namebuf [1024]byte
|
||||||
|
sz := int32(copy(namebuf[:], path))
|
||||||
|
A2e(namebuf[:sz])
|
||||||
|
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sz)
|
||||||
|
parms[1] = unsafe.Pointer(&namebuf[0])
|
||||||
|
parms[2] = unsafe.Pointer(&attr_sz)
|
||||||
|
parms[3] = unsafe.Pointer(attr)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4LCR)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxFchattr(fd int32, attr *Bpxyatt_t) (rv int32, rc int32, rn int32) {
|
||||||
|
attr_sz := uint32(unsafe.Sizeof(*attr))
|
||||||
|
var parms [6]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&fd)
|
||||||
|
parms[1] = unsafe.Pointer(&attr_sz)
|
||||||
|
parms[2] = unsafe.Pointer(attr)
|
||||||
|
parms[3] = unsafe.Pointer(&rv)
|
||||||
|
parms[4] = unsafe.Pointer(&rc)
|
||||||
|
parms[5] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4FCR)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func BpxCondTimedWait(sec uint32, nsec uint32, events uint32, secrem *uint32, nsecrem *uint32) (rv int32, rc int32, rn int32) {
|
||||||
|
var parms [8]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&sec)
|
||||||
|
parms[1] = unsafe.Pointer(&nsec)
|
||||||
|
parms[2] = unsafe.Pointer(&events)
|
||||||
|
parms[3] = unsafe.Pointer(secrem)
|
||||||
|
parms[4] = unsafe.Pointer(nsecrem)
|
||||||
|
parms[5] = unsafe.Pointer(&rv)
|
||||||
|
parms[6] = unsafe.Pointer(&rc)
|
||||||
|
parms[7] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4CTW)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
func BpxGetthent(in *Pgtha, outlen *uint32, out unsafe.Pointer) (rv int32, rc int32, rn int32) {
|
||||||
|
var parms [7]unsafe.Pointer
|
||||||
|
inlen := uint32(26) // nothing else will work. Go says Pgtha is 28-byte because of alignment, but Pgtha is "packed" and must be 26-byte
|
||||||
|
parms[0] = unsafe.Pointer(&inlen)
|
||||||
|
parms[1] = unsafe.Pointer(&in)
|
||||||
|
parms[2] = unsafe.Pointer(outlen)
|
||||||
|
parms[3] = unsafe.Pointer(&out)
|
||||||
|
parms[4] = unsafe.Pointer(&rv)
|
||||||
|
parms[5] = unsafe.Pointer(&rc)
|
||||||
|
parms[6] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4GTH)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
func ZosJobname() (jobname string, err error) {
|
||||||
|
var pgtha Pgtha
|
||||||
|
pgtha.Pid = uint32(Getpid())
|
||||||
|
pgtha.Accesspid = PGTHA_CURRENT
|
||||||
|
pgtha.Flag1 = PGTHA_PROCESS
|
||||||
|
var out [256]byte
|
||||||
|
var outlen uint32
|
||||||
|
outlen = 256
|
||||||
|
rv, rc, rn := BpxGetthent(&pgtha, &outlen, unsafe.Pointer(&out[0]))
|
||||||
|
if rv == 0 {
|
||||||
|
gthc := []byte{0x87, 0xa3, 0x88, 0x83} // 'gthc' in ebcdic
|
||||||
|
ix := bytes.Index(out[:], gthc)
|
||||||
|
if ix == -1 {
|
||||||
|
err = fmt.Errorf("BPX4GTH: gthc return data not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jn := out[ix+80 : ix+88] // we didn't declare Pgthc, but jobname is 8-byte at offset 80
|
||||||
|
E2a(jn)
|
||||||
|
jobname = string(bytes.TrimRight(jn, " "))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("BPX4GTH: rc=%d errno=%d reason=code=0x%x", rv, rc, rn)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func Bpx4ptq(code int32, data string) (rv int32, rc int32, rn int32) {
|
||||||
|
var userdata [8]byte
|
||||||
|
var parms [5]unsafe.Pointer
|
||||||
|
copy(userdata[:], data+" ")
|
||||||
|
A2e(userdata[:])
|
||||||
|
parms[0] = unsafe.Pointer(&code)
|
||||||
|
parms[1] = unsafe.Pointer(&userdata[0])
|
||||||
|
parms[2] = unsafe.Pointer(&rv)
|
||||||
|
parms[3] = unsafe.Pointer(&rc)
|
||||||
|
parms[4] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4PTQ)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PT_TRACE_ME = 0 // Debug this process
|
||||||
|
PT_READ_I = 1 // Read a full word
|
||||||
|
PT_READ_D = 2 // Read a full word
|
||||||
|
PT_READ_U = 3 // Read control info
|
||||||
|
PT_WRITE_I = 4 //Write a full word
|
||||||
|
PT_WRITE_D = 5 //Write a full word
|
||||||
|
PT_CONTINUE = 7 //Continue the process
|
||||||
|
PT_KILL = 8 //Terminate the process
|
||||||
|
PT_READ_GPR = 11 // Read GPR, CR, PSW
|
||||||
|
PT_READ_FPR = 12 // Read FPR
|
||||||
|
PT_READ_VR = 13 // Read VR
|
||||||
|
PT_WRITE_GPR = 14 // Write GPR, CR, PSW
|
||||||
|
PT_WRITE_FPR = 15 // Write FPR
|
||||||
|
PT_WRITE_VR = 16 // Write VR
|
||||||
|
PT_READ_BLOCK = 17 // Read storage
|
||||||
|
PT_WRITE_BLOCK = 19 // Write storage
|
||||||
|
PT_READ_GPRH = 20 // Read GPRH
|
||||||
|
PT_WRITE_GPRH = 21 // Write GPRH
|
||||||
|
PT_REGHSET = 22 // Read all GPRHs
|
||||||
|
PT_ATTACH = 30 // Attach to a process
|
||||||
|
PT_DETACH = 31 // Detach from a process
|
||||||
|
PT_REGSET = 32 // Read all GPRs
|
||||||
|
PT_REATTACH = 33 // Reattach to a process
|
||||||
|
PT_LDINFO = 34 // Read loader info
|
||||||
|
PT_MULTI = 35 // Multi process mode
|
||||||
|
PT_LD64INFO = 36 // RMODE64 Info Area
|
||||||
|
PT_BLOCKREQ = 40 // Block request
|
||||||
|
PT_THREAD_INFO = 60 // Read thread info
|
||||||
|
PT_THREAD_MODIFY = 61
|
||||||
|
PT_THREAD_READ_FOCUS = 62
|
||||||
|
PT_THREAD_WRITE_FOCUS = 63
|
||||||
|
PT_THREAD_HOLD = 64
|
||||||
|
PT_THREAD_SIGNAL = 65
|
||||||
|
PT_EXPLAIN = 66
|
||||||
|
PT_EVENTS = 67
|
||||||
|
PT_THREAD_INFO_EXTENDED = 68
|
||||||
|
PT_REATTACH2 = 71
|
||||||
|
PT_CAPTURE = 72
|
||||||
|
PT_UNCAPTURE = 73
|
||||||
|
PT_GET_THREAD_TCB = 74
|
||||||
|
PT_GET_ALET = 75
|
||||||
|
PT_SWAPIN = 76
|
||||||
|
PT_EXTENDED_EVENT = 98
|
||||||
|
PT_RECOVER = 99 // Debug a program check
|
||||||
|
PT_GPR0 = 0 // General purpose register 0
|
||||||
|
PT_GPR1 = 1 // General purpose register 1
|
||||||
|
PT_GPR2 = 2 // General purpose register 2
|
||||||
|
PT_GPR3 = 3 // General purpose register 3
|
||||||
|
PT_GPR4 = 4 // General purpose register 4
|
||||||
|
PT_GPR5 = 5 // General purpose register 5
|
||||||
|
PT_GPR6 = 6 // General purpose register 6
|
||||||
|
PT_GPR7 = 7 // General purpose register 7
|
||||||
|
PT_GPR8 = 8 // General purpose register 8
|
||||||
|
PT_GPR9 = 9 // General purpose register 9
|
||||||
|
PT_GPR10 = 10 // General purpose register 10
|
||||||
|
PT_GPR11 = 11 // General purpose register 11
|
||||||
|
PT_GPR12 = 12 // General purpose register 12
|
||||||
|
PT_GPR13 = 13 // General purpose register 13
|
||||||
|
PT_GPR14 = 14 // General purpose register 14
|
||||||
|
PT_GPR15 = 15 // General purpose register 15
|
||||||
|
PT_FPR0 = 16 // Floating point register 0
|
||||||
|
PT_FPR1 = 17 // Floating point register 1
|
||||||
|
PT_FPR2 = 18 // Floating point register 2
|
||||||
|
PT_FPR3 = 19 // Floating point register 3
|
||||||
|
PT_FPR4 = 20 // Floating point register 4
|
||||||
|
PT_FPR5 = 21 // Floating point register 5
|
||||||
|
PT_FPR6 = 22 // Floating point register 6
|
||||||
|
PT_FPR7 = 23 // Floating point register 7
|
||||||
|
PT_FPR8 = 24 // Floating point register 8
|
||||||
|
PT_FPR9 = 25 // Floating point register 9
|
||||||
|
PT_FPR10 = 26 // Floating point register 10
|
||||||
|
PT_FPR11 = 27 // Floating point register 11
|
||||||
|
PT_FPR12 = 28 // Floating point register 12
|
||||||
|
PT_FPR13 = 29 // Floating point register 13
|
||||||
|
PT_FPR14 = 30 // Floating point register 14
|
||||||
|
PT_FPR15 = 31 // Floating point register 15
|
||||||
|
PT_FPC = 32 // Floating point control register
|
||||||
|
PT_PSW = 40 // PSW
|
||||||
|
PT_PSW0 = 40 // Left half of the PSW
|
||||||
|
PT_PSW1 = 41 // Right half of the PSW
|
||||||
|
PT_CR0 = 42 // Control register 0
|
||||||
|
PT_CR1 = 43 // Control register 1
|
||||||
|
PT_CR2 = 44 // Control register 2
|
||||||
|
PT_CR3 = 45 // Control register 3
|
||||||
|
PT_CR4 = 46 // Control register 4
|
||||||
|
PT_CR5 = 47 // Control register 5
|
||||||
|
PT_CR6 = 48 // Control register 6
|
||||||
|
PT_CR7 = 49 // Control register 7
|
||||||
|
PT_CR8 = 50 // Control register 8
|
||||||
|
PT_CR9 = 51 // Control register 9
|
||||||
|
PT_CR10 = 52 // Control register 10
|
||||||
|
PT_CR11 = 53 // Control register 11
|
||||||
|
PT_CR12 = 54 // Control register 12
|
||||||
|
PT_CR13 = 55 // Control register 13
|
||||||
|
PT_CR14 = 56 // Control register 14
|
||||||
|
PT_CR15 = 57 // Control register 15
|
||||||
|
PT_GPRH0 = 58 // GP High register 0
|
||||||
|
PT_GPRH1 = 59 // GP High register 1
|
||||||
|
PT_GPRH2 = 60 // GP High register 2
|
||||||
|
PT_GPRH3 = 61 // GP High register 3
|
||||||
|
PT_GPRH4 = 62 // GP High register 4
|
||||||
|
PT_GPRH5 = 63 // GP High register 5
|
||||||
|
PT_GPRH6 = 64 // GP High register 6
|
||||||
|
PT_GPRH7 = 65 // GP High register 7
|
||||||
|
PT_GPRH8 = 66 // GP High register 8
|
||||||
|
PT_GPRH9 = 67 // GP High register 9
|
||||||
|
PT_GPRH10 = 68 // GP High register 10
|
||||||
|
PT_GPRH11 = 69 // GP High register 11
|
||||||
|
PT_GPRH12 = 70 // GP High register 12
|
||||||
|
PT_GPRH13 = 71 // GP High register 13
|
||||||
|
PT_GPRH14 = 72 // GP High register 14
|
||||||
|
PT_GPRH15 = 73 // GP High register 15
|
||||||
|
PT_VR0 = 74 // Vector register 0
|
||||||
|
PT_VR1 = 75 // Vector register 1
|
||||||
|
PT_VR2 = 76 // Vector register 2
|
||||||
|
PT_VR3 = 77 // Vector register 3
|
||||||
|
PT_VR4 = 78 // Vector register 4
|
||||||
|
PT_VR5 = 79 // Vector register 5
|
||||||
|
PT_VR6 = 80 // Vector register 6
|
||||||
|
PT_VR7 = 81 // Vector register 7
|
||||||
|
PT_VR8 = 82 // Vector register 8
|
||||||
|
PT_VR9 = 83 // Vector register 9
|
||||||
|
PT_VR10 = 84 // Vector register 10
|
||||||
|
PT_VR11 = 85 // Vector register 11
|
||||||
|
PT_VR12 = 86 // Vector register 12
|
||||||
|
PT_VR13 = 87 // Vector register 13
|
||||||
|
PT_VR14 = 88 // Vector register 14
|
||||||
|
PT_VR15 = 89 // Vector register 15
|
||||||
|
PT_VR16 = 90 // Vector register 16
|
||||||
|
PT_VR17 = 91 // Vector register 17
|
||||||
|
PT_VR18 = 92 // Vector register 18
|
||||||
|
PT_VR19 = 93 // Vector register 19
|
||||||
|
PT_VR20 = 94 // Vector register 20
|
||||||
|
PT_VR21 = 95 // Vector register 21
|
||||||
|
PT_VR22 = 96 // Vector register 22
|
||||||
|
PT_VR23 = 97 // Vector register 23
|
||||||
|
PT_VR24 = 98 // Vector register 24
|
||||||
|
PT_VR25 = 99 // Vector register 25
|
||||||
|
PT_VR26 = 100 // Vector register 26
|
||||||
|
PT_VR27 = 101 // Vector register 27
|
||||||
|
PT_VR28 = 102 // Vector register 28
|
||||||
|
PT_VR29 = 103 // Vector register 29
|
||||||
|
PT_VR30 = 104 // Vector register 30
|
||||||
|
PT_VR31 = 105 // Vector register 31
|
||||||
|
PT_PSWG = 106 // PSWG
|
||||||
|
PT_PSWG0 = 106 // Bytes 0-3
|
||||||
|
PT_PSWG1 = 107 // Bytes 4-7
|
||||||
|
PT_PSWG2 = 108 // Bytes 8-11 (IA high word)
|
||||||
|
PT_PSWG3 = 109 // Bytes 12-15 (IA low word)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Bpx4ptr(request int32, pid int32, addr unsafe.Pointer, data unsafe.Pointer, buffer unsafe.Pointer) (rv int32, rc int32, rn int32) {
|
||||||
|
var parms [8]unsafe.Pointer
|
||||||
|
parms[0] = unsafe.Pointer(&request)
|
||||||
|
parms[1] = unsafe.Pointer(&pid)
|
||||||
|
parms[2] = unsafe.Pointer(&addr)
|
||||||
|
parms[3] = unsafe.Pointer(&data)
|
||||||
|
parms[4] = unsafe.Pointer(&buffer)
|
||||||
|
parms[5] = unsafe.Pointer(&rv)
|
||||||
|
parms[6] = unsafe.Pointer(&rc)
|
||||||
|
parms[7] = unsafe.Pointer(&rn)
|
||||||
|
bpxcall(parms[:], BPX4PTR)
|
||||||
|
return rv, rc, rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU8(val uint8, dest []uint8) int {
|
||||||
|
if len(dest) < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dest[0] = val
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU8Arr(src, dest []uint8) int {
|
||||||
|
if len(dest) < len(src) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i, v := range src {
|
||||||
|
dest[i] = v
|
||||||
|
}
|
||||||
|
return len(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU16(val uint16, dest []uint16) int {
|
||||||
|
if len(dest) < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dest[0] = val
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU32(val uint32, dest []uint32) int {
|
||||||
|
if len(dest) < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dest[0] = val
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU32Arr(src, dest []uint32) int {
|
||||||
|
if len(dest) < len(src) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i, v := range src {
|
||||||
|
dest[i] = v
|
||||||
|
}
|
||||||
|
return len(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyU64(val uint64, dest []uint64) int {
|
||||||
|
if len(dest) < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dest[0] = val
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// function to call USS assembly language services
|
||||||
|
//
|
||||||
|
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bit64env.htm
|
||||||
|
//
|
||||||
|
// arg1 unsafe.Pointer array that ressembles an OS PLIST
|
||||||
|
//
|
||||||
|
// arg2 function offset as in
|
||||||
|
// doc: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_3.1.0/com.ibm.zos.v3r1.bpxb100/bpx2cr_List_of_offsets.htm
|
||||||
|
//
|
||||||
|
// func bpxcall(plist []unsafe.Pointer, bpx_offset int64)
|
||||||
|
|
||||||
|
TEXT ·bpxcall(SB), NOSPLIT|NOFRAME, $0
|
||||||
|
MOVD plist_base+0(FP), R1 // r1 points to plist
|
||||||
|
MOVD bpx_offset+24(FP), R2 // r2 offset to BPX vector table
|
||||||
|
MOVD R14, R7 // save r14
|
||||||
|
MOVD R15, R8 // save r15
|
||||||
|
MOVWZ 16(R0), R9
|
||||||
|
MOVWZ 544(R9), R9
|
||||||
|
MOVWZ 24(R9), R9 // call vector in r9
|
||||||
|
ADD R2, R9 // add offset to vector table
|
||||||
|
MOVWZ (R9), R9 // r9 points to entry point
|
||||||
|
BYTE $0x0D // BL R14,R9 --> basr r14,r9
|
||||||
|
BYTE $0xE9 // clobbers 0,1,14,15
|
||||||
|
MOVD R8, R15 // restore 15
|
||||||
|
JMP R7 // return via saved return address
|
||||||
|
|
||||||
|
// func A2e(arr [] byte)
|
||||||
|
// code page conversion from 819 to 1047
|
||||||
|
TEXT ·A2e(SB), NOSPLIT|NOFRAME, $0
|
||||||
|
MOVD arg_base+0(FP), R2 // pointer to arry of characters
|
||||||
|
MOVD arg_len+8(FP), R3 // count
|
||||||
|
XOR R0, R0
|
||||||
|
XOR R1, R1
|
||||||
|
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
|
||||||
|
|
||||||
|
// ASCII -> EBCDIC conversion table:
|
||||||
|
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
|
||||||
|
BYTE $0x37; BYTE $0x2d; BYTE $0x2e; BYTE $0x2f
|
||||||
|
BYTE $0x16; BYTE $0x05; BYTE $0x15; BYTE $0x0b
|
||||||
|
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
|
||||||
|
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
|
||||||
|
BYTE $0x3c; BYTE $0x3d; BYTE $0x32; BYTE $0x26
|
||||||
|
BYTE $0x18; BYTE $0x19; BYTE $0x3f; BYTE $0x27
|
||||||
|
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
|
||||||
|
BYTE $0x40; BYTE $0x5a; BYTE $0x7f; BYTE $0x7b
|
||||||
|
BYTE $0x5b; BYTE $0x6c; BYTE $0x50; BYTE $0x7d
|
||||||
|
BYTE $0x4d; BYTE $0x5d; BYTE $0x5c; BYTE $0x4e
|
||||||
|
BYTE $0x6b; BYTE $0x60; BYTE $0x4b; BYTE $0x61
|
||||||
|
BYTE $0xf0; BYTE $0xf1; BYTE $0xf2; BYTE $0xf3
|
||||||
|
BYTE $0xf4; BYTE $0xf5; BYTE $0xf6; BYTE $0xf7
|
||||||
|
BYTE $0xf8; BYTE $0xf9; BYTE $0x7a; BYTE $0x5e
|
||||||
|
BYTE $0x4c; BYTE $0x7e; BYTE $0x6e; BYTE $0x6f
|
||||||
|
BYTE $0x7c; BYTE $0xc1; BYTE $0xc2; BYTE $0xc3
|
||||||
|
BYTE $0xc4; BYTE $0xc5; BYTE $0xc6; BYTE $0xc7
|
||||||
|
BYTE $0xc8; BYTE $0xc9; BYTE $0xd1; BYTE $0xd2
|
||||||
|
BYTE $0xd3; BYTE $0xd4; BYTE $0xd5; BYTE $0xd6
|
||||||
|
BYTE $0xd7; BYTE $0xd8; BYTE $0xd9; BYTE $0xe2
|
||||||
|
BYTE $0xe3; BYTE $0xe4; BYTE $0xe5; BYTE $0xe6
|
||||||
|
BYTE $0xe7; BYTE $0xe8; BYTE $0xe9; BYTE $0xad
|
||||||
|
BYTE $0xe0; BYTE $0xbd; BYTE $0x5f; BYTE $0x6d
|
||||||
|
BYTE $0x79; BYTE $0x81; BYTE $0x82; BYTE $0x83
|
||||||
|
BYTE $0x84; BYTE $0x85; BYTE $0x86; BYTE $0x87
|
||||||
|
BYTE $0x88; BYTE $0x89; BYTE $0x91; BYTE $0x92
|
||||||
|
BYTE $0x93; BYTE $0x94; BYTE $0x95; BYTE $0x96
|
||||||
|
BYTE $0x97; BYTE $0x98; BYTE $0x99; BYTE $0xa2
|
||||||
|
BYTE $0xa3; BYTE $0xa4; BYTE $0xa5; BYTE $0xa6
|
||||||
|
BYTE $0xa7; BYTE $0xa8; BYTE $0xa9; BYTE $0xc0
|
||||||
|
BYTE $0x4f; BYTE $0xd0; BYTE $0xa1; BYTE $0x07
|
||||||
|
BYTE $0x20; BYTE $0x21; BYTE $0x22; BYTE $0x23
|
||||||
|
BYTE $0x24; BYTE $0x25; BYTE $0x06; BYTE $0x17
|
||||||
|
BYTE $0x28; BYTE $0x29; BYTE $0x2a; BYTE $0x2b
|
||||||
|
BYTE $0x2c; BYTE $0x09; BYTE $0x0a; BYTE $0x1b
|
||||||
|
BYTE $0x30; BYTE $0x31; BYTE $0x1a; BYTE $0x33
|
||||||
|
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x08
|
||||||
|
BYTE $0x38; BYTE $0x39; BYTE $0x3a; BYTE $0x3b
|
||||||
|
BYTE $0x04; BYTE $0x14; BYTE $0x3e; BYTE $0xff
|
||||||
|
BYTE $0x41; BYTE $0xaa; BYTE $0x4a; BYTE $0xb1
|
||||||
|
BYTE $0x9f; BYTE $0xb2; BYTE $0x6a; BYTE $0xb5
|
||||||
|
BYTE $0xbb; BYTE $0xb4; BYTE $0x9a; BYTE $0x8a
|
||||||
|
BYTE $0xb0; BYTE $0xca; BYTE $0xaf; BYTE $0xbc
|
||||||
|
BYTE $0x90; BYTE $0x8f; BYTE $0xea; BYTE $0xfa
|
||||||
|
BYTE $0xbe; BYTE $0xa0; BYTE $0xb6; BYTE $0xb3
|
||||||
|
BYTE $0x9d; BYTE $0xda; BYTE $0x9b; BYTE $0x8b
|
||||||
|
BYTE $0xb7; BYTE $0xb8; BYTE $0xb9; BYTE $0xab
|
||||||
|
BYTE $0x64; BYTE $0x65; BYTE $0x62; BYTE $0x66
|
||||||
|
BYTE $0x63; BYTE $0x67; BYTE $0x9e; BYTE $0x68
|
||||||
|
BYTE $0x74; BYTE $0x71; BYTE $0x72; BYTE $0x73
|
||||||
|
BYTE $0x78; BYTE $0x75; BYTE $0x76; BYTE $0x77
|
||||||
|
BYTE $0xac; BYTE $0x69; BYTE $0xed; BYTE $0xee
|
||||||
|
BYTE $0xeb; BYTE $0xef; BYTE $0xec; BYTE $0xbf
|
||||||
|
BYTE $0x80; BYTE $0xfd; BYTE $0xfe; BYTE $0xfb
|
||||||
|
BYTE $0xfc; BYTE $0xba; BYTE $0xae; BYTE $0x59
|
||||||
|
BYTE $0x44; BYTE $0x45; BYTE $0x42; BYTE $0x46
|
||||||
|
BYTE $0x43; BYTE $0x47; BYTE $0x9c; BYTE $0x48
|
||||||
|
BYTE $0x54; BYTE $0x51; BYTE $0x52; BYTE $0x53
|
||||||
|
BYTE $0x58; BYTE $0x55; BYTE $0x56; BYTE $0x57
|
||||||
|
BYTE $0x8c; BYTE $0x49; BYTE $0xcd; BYTE $0xce
|
||||||
|
BYTE $0xcb; BYTE $0xcf; BYTE $0xcc; BYTE $0xe1
|
||||||
|
BYTE $0x70; BYTE $0xdd; BYTE $0xde; BYTE $0xdb
|
||||||
|
BYTE $0xdc; BYTE $0x8d; BYTE $0x8e; BYTE $0xdf
|
||||||
|
|
||||||
|
retry:
|
||||||
|
WORD $0xB9931022 // TROO 2,2,b'0001'
|
||||||
|
BVS retry
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func e2a(arr [] byte)
|
||||||
|
// code page conversion from 1047 to 819
|
||||||
|
TEXT ·E2a(SB), NOSPLIT|NOFRAME, $0
|
||||||
|
MOVD arg_base+0(FP), R2 // pointer to arry of characters
|
||||||
|
MOVD arg_len+8(FP), R3 // count
|
||||||
|
XOR R0, R0
|
||||||
|
XOR R1, R1
|
||||||
|
BYTE $0xA7; BYTE $0x15; BYTE $0x00; BYTE $0x82 // BRAS 1,(2+(256/2))
|
||||||
|
|
||||||
|
// EBCDIC -> ASCII conversion table:
|
||||||
|
BYTE $0x00; BYTE $0x01; BYTE $0x02; BYTE $0x03
|
||||||
|
BYTE $0x9c; BYTE $0x09; BYTE $0x86; BYTE $0x7f
|
||||||
|
BYTE $0x97; BYTE $0x8d; BYTE $0x8e; BYTE $0x0b
|
||||||
|
BYTE $0x0c; BYTE $0x0d; BYTE $0x0e; BYTE $0x0f
|
||||||
|
BYTE $0x10; BYTE $0x11; BYTE $0x12; BYTE $0x13
|
||||||
|
BYTE $0x9d; BYTE $0x0a; BYTE $0x08; BYTE $0x87
|
||||||
|
BYTE $0x18; BYTE $0x19; BYTE $0x92; BYTE $0x8f
|
||||||
|
BYTE $0x1c; BYTE $0x1d; BYTE $0x1e; BYTE $0x1f
|
||||||
|
BYTE $0x80; BYTE $0x81; BYTE $0x82; BYTE $0x83
|
||||||
|
BYTE $0x84; BYTE $0x85; BYTE $0x17; BYTE $0x1b
|
||||||
|
BYTE $0x88; BYTE $0x89; BYTE $0x8a; BYTE $0x8b
|
||||||
|
BYTE $0x8c; BYTE $0x05; BYTE $0x06; BYTE $0x07
|
||||||
|
BYTE $0x90; BYTE $0x91; BYTE $0x16; BYTE $0x93
|
||||||
|
BYTE $0x94; BYTE $0x95; BYTE $0x96; BYTE $0x04
|
||||||
|
BYTE $0x98; BYTE $0x99; BYTE $0x9a; BYTE $0x9b
|
||||||
|
BYTE $0x14; BYTE $0x15; BYTE $0x9e; BYTE $0x1a
|
||||||
|
BYTE $0x20; BYTE $0xa0; BYTE $0xe2; BYTE $0xe4
|
||||||
|
BYTE $0xe0; BYTE $0xe1; BYTE $0xe3; BYTE $0xe5
|
||||||
|
BYTE $0xe7; BYTE $0xf1; BYTE $0xa2; BYTE $0x2e
|
||||||
|
BYTE $0x3c; BYTE $0x28; BYTE $0x2b; BYTE $0x7c
|
||||||
|
BYTE $0x26; BYTE $0xe9; BYTE $0xea; BYTE $0xeb
|
||||||
|
BYTE $0xe8; BYTE $0xed; BYTE $0xee; BYTE $0xef
|
||||||
|
BYTE $0xec; BYTE $0xdf; BYTE $0x21; BYTE $0x24
|
||||||
|
BYTE $0x2a; BYTE $0x29; BYTE $0x3b; BYTE $0x5e
|
||||||
|
BYTE $0x2d; BYTE $0x2f; BYTE $0xc2; BYTE $0xc4
|
||||||
|
BYTE $0xc0; BYTE $0xc1; BYTE $0xc3; BYTE $0xc5
|
||||||
|
BYTE $0xc7; BYTE $0xd1; BYTE $0xa6; BYTE $0x2c
|
||||||
|
BYTE $0x25; BYTE $0x5f; BYTE $0x3e; BYTE $0x3f
|
||||||
|
BYTE $0xf8; BYTE $0xc9; BYTE $0xca; BYTE $0xcb
|
||||||
|
BYTE $0xc8; BYTE $0xcd; BYTE $0xce; BYTE $0xcf
|
||||||
|
BYTE $0xcc; BYTE $0x60; BYTE $0x3a; BYTE $0x23
|
||||||
|
BYTE $0x40; BYTE $0x27; BYTE $0x3d; BYTE $0x22
|
||||||
|
BYTE $0xd8; BYTE $0x61; BYTE $0x62; BYTE $0x63
|
||||||
|
BYTE $0x64; BYTE $0x65; BYTE $0x66; BYTE $0x67
|
||||||
|
BYTE $0x68; BYTE $0x69; BYTE $0xab; BYTE $0xbb
|
||||||
|
BYTE $0xf0; BYTE $0xfd; BYTE $0xfe; BYTE $0xb1
|
||||||
|
BYTE $0xb0; BYTE $0x6a; BYTE $0x6b; BYTE $0x6c
|
||||||
|
BYTE $0x6d; BYTE $0x6e; BYTE $0x6f; BYTE $0x70
|
||||||
|
BYTE $0x71; BYTE $0x72; BYTE $0xaa; BYTE $0xba
|
||||||
|
BYTE $0xe6; BYTE $0xb8; BYTE $0xc6; BYTE $0xa4
|
||||||
|
BYTE $0xb5; BYTE $0x7e; BYTE $0x73; BYTE $0x74
|
||||||
|
BYTE $0x75; BYTE $0x76; BYTE $0x77; BYTE $0x78
|
||||||
|
BYTE $0x79; BYTE $0x7a; BYTE $0xa1; BYTE $0xbf
|
||||||
|
BYTE $0xd0; BYTE $0x5b; BYTE $0xde; BYTE $0xae
|
||||||
|
BYTE $0xac; BYTE $0xa3; BYTE $0xa5; BYTE $0xb7
|
||||||
|
BYTE $0xa9; BYTE $0xa7; BYTE $0xb6; BYTE $0xbc
|
||||||
|
BYTE $0xbd; BYTE $0xbe; BYTE $0xdd; BYTE $0xa8
|
||||||
|
BYTE $0xaf; BYTE $0x5d; BYTE $0xb4; BYTE $0xd7
|
||||||
|
BYTE $0x7b; BYTE $0x41; BYTE $0x42; BYTE $0x43
|
||||||
|
BYTE $0x44; BYTE $0x45; BYTE $0x46; BYTE $0x47
|
||||||
|
BYTE $0x48; BYTE $0x49; BYTE $0xad; BYTE $0xf4
|
||||||
|
BYTE $0xf6; BYTE $0xf2; BYTE $0xf3; BYTE $0xf5
|
||||||
|
BYTE $0x7d; BYTE $0x4a; BYTE $0x4b; BYTE $0x4c
|
||||||
|
BYTE $0x4d; BYTE $0x4e; BYTE $0x4f; BYTE $0x50
|
||||||
|
BYTE $0x51; BYTE $0x52; BYTE $0xb9; BYTE $0xfb
|
||||||
|
BYTE $0xfc; BYTE $0xf9; BYTE $0xfa; BYTE $0xff
|
||||||
|
BYTE $0x5c; BYTE $0xf7; BYTE $0x53; BYTE $0x54
|
||||||
|
BYTE $0x55; BYTE $0x56; BYTE $0x57; BYTE $0x58
|
||||||
|
BYTE $0x59; BYTE $0x5a; BYTE $0xb2; BYTE $0xd4
|
||||||
|
BYTE $0xd6; BYTE $0xd2; BYTE $0xd3; BYTE $0xd5
|
||||||
|
BYTE $0x30; BYTE $0x31; BYTE $0x32; BYTE $0x33
|
||||||
|
BYTE $0x34; BYTE $0x35; BYTE $0x36; BYTE $0x37
|
||||||
|
BYTE $0x38; BYTE $0x39; BYTE $0xb3; BYTE $0xdb
|
||||||
|
BYTE $0xdc; BYTE $0xd9; BYTE $0xda; BYTE $0x9f
|
||||||
|
|
||||||
|
retry:
|
||||||
|
WORD $0xB9931022 // TROO 2,2,b'0001'
|
||||||
|
BVS retry
|
||||||
|
RET
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build freebsd
|
//go:build freebsd
|
||||||
// +build freebsd
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix && ppc
|
//go:build aix && ppc
|
||||||
// +build aix,ppc
|
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by AIX.
|
// encoding used by AIX.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix && ppc64
|
//go:build aix && ppc64
|
||||||
// +build aix,ppc64
|
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used AIX.
|
// encoding used AIX.
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue