mirror of
https://github.com/drone/drone-cli.git
synced 2024-05-12 18:46:02 +02:00
copy 0.6 cli to own repo
This commit is contained in:
parent
89df6891d1
commit
615bf50efb
27
.drone.sh
27
.drone.sh
|
@ -2,20 +2,23 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
mkdir bin
|
||||
mkdir dist
|
||||
|
||||
# compile drone for all architectures
|
||||
GOOS=linux GOARCH=amd64 go build -o ./bin/linux_amd64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./bin/darwin_amd64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=windows GOARCH=amd64 go build -o ./bin/windows_amd64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o release/linux/amd64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o release/linux/arm64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -o release/linux/arm/drone github.com/drone/drone-cli/drone
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o release/windows/amd64/drone github.com/drone/drone-cli/drone
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o release/darwin/amd64/drone github.com/drone/drone-cli/drone
|
||||
|
||||
# tar binary files prior to upload
|
||||
tar -cvzf dist/drone_linux_amd64.tar.gz --directory=bin/linux_amd64 drone
|
||||
tar -cvzf dist/drone_darwin_amd64.tar.gz --directory=bin/darwin_amd64 drone
|
||||
tar -cvzf dist/drone_windows_amd64.tar.gz --directory=bin/windows_amd64 drone
|
||||
tar -cvzf release/linux/amd64/drone.tar.gz -C release/linux/amd64 drone
|
||||
tar -cvzf release/linux/arm64/drone.tar.gz -C release/linux/arm64 drone
|
||||
tar -cvzf release/linux/arm/drone.tar.gz -C release/linux/arm drone
|
||||
tar -cvzf release/windows/amd64/drone.tar.gz -C release/windows/amd64 drone
|
||||
tar -cvzf release/darwin/amd64/drone.tar.gz -C release/darwin/amd64 drone
|
||||
|
||||
# generate shas for tar files
|
||||
sha256sum ./dist/drone_linux_amd64.tar.gz > ./dist/drone_linux_amd64.sha256
|
||||
sha256sum ./dist/drone_darwin_amd64.tar.gz > ./dist/drone_darwin_amd64.sha256
|
||||
sha256sum ./dist/drone_windows_amd64.tar.gz > ./dist/drone_windows_amd64.sha256
|
||||
sha256sum release/linux/amd64/drone.tar.gz > release/linux/amd64/drone.sha256
|
||||
sha256sum release/linux/arm64/drone.tar.gz > release/linux/arm64/drone.sha256
|
||||
sha256sum release/linux/arm/drone.tar.gz > release/linux/arm/drone.sha256
|
||||
sha256sum release/windows/amd64/drone.tar.gz > release/windows/amd64/drone.sha256
|
||||
sha256sum release/darwin/amd64/drone.tar.gz > release/darwin/amd64/drone.sha256
|
||||
|
|
11
.drone.yml
11
.drone.yml
|
@ -8,14 +8,9 @@ pipeline:
|
|||
commands:
|
||||
- cd drone
|
||||
- go test
|
||||
dist:
|
||||
|
||||
build:
|
||||
image: golang:1.6
|
||||
commands: ./.drone.sh
|
||||
commands: bash .drone.sh
|
||||
when:
|
||||
event: push
|
||||
|
||||
# publish:
|
||||
# image: s3
|
||||
# when:
|
||||
# event: push
|
||||
# branch: master
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Binary file(s)
|
||||
drone/drone
|
||||
dist/
|
||||
bin/
|
||||
|
||||
NOTES.md
|
||||
drone/.drone.yml
|
44
README.md
44
README.md
|
@ -1,43 +1 @@
|
|||
# drone-cli
|
||||
Drone command-line interface
|
||||
|
||||
### System Requirements
|
||||
|
||||
The Drone command-line utility requires **Docker 1.6** or higher. If you are using Windows or Mac you can install Docker using the [Docker Toolbox](https://www.docker.com/docker-toolbox).
|
||||
|
||||
### Installation
|
||||
|
||||
**Linux**
|
||||
|
||||
Download and install the x64 linux binary:
|
||||
|
||||
```
|
||||
curl http://downloads.drone.io/drone-cli/drone_linux_amd64.tar.gz | tar zx
|
||||
sudo install -t /usr/local/bin drone
|
||||
```
|
||||
|
||||
**OSX**
|
||||
|
||||
Download and install using Homebrew:
|
||||
|
||||
```
|
||||
brew tap drone/drone
|
||||
brew install drone
|
||||
```
|
||||
|
||||
Or manually download and install the binary:
|
||||
|
||||
```
|
||||
curl http://downloads.drone.io/drone-cli/drone_darwin_amd64.tar.gz | tar zx
|
||||
sudo cp drone /usr/local/bin
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
You must provide the command line utility with the Drone server URL and a valid API token. You can get your API token in the Drone profile page.
|
||||
|
||||
```
|
||||
export DRONE_TOKEN=
|
||||
export DRONE_SERVER=
|
||||
```
|
||||
|
||||
Command line client for the Drone continuous integration server. Please see the official documentation at http://docs.drone.io/cli-installation/
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var agentCmd = cli.Command{
|
||||
Name: "agents",
|
||||
Usage: "manage agents",
|
||||
Action: agentList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplAgentList,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func agentList(c *cli.Context) error {
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agents, err := client.AgentList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Funcs(funcMap).Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, agent := range agents {
|
||||
tmpl.Execute(os.Stdout, agent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for build list information
|
||||
var tmplAgentList = "\x1b[33m{{ .Address }} \x1b[0m" + `
|
||||
Platform: {{ .Platform }}
|
||||
Capacity: {{ .Capacity }} concurrent build(s)
|
||||
Pinged: {{ since .Updated }} ago
|
||||
Uptime: {{ since .Created }}
|
||||
`
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"since": func(t int64) string {
|
||||
d := time.Now().Sub(time.Unix(t, 0))
|
||||
return d.String()
|
||||
},
|
||||
}
|
341
drone/build.go
341
drone/build.go
|
@ -1,341 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var buildCmd = cli.Command{
|
||||
Name: "build",
|
||||
Usage: "manage builds",
|
||||
Subcommands: []cli.Command{
|
||||
|
||||
// list command
|
||||
cli.Command{
|
||||
Name: "list",
|
||||
Usage: "show build history",
|
||||
Action: buildList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildList,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "event",
|
||||
Usage: "event filter",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "status",
|
||||
Usage: "status filter",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "limit the list size",
|
||||
Value: 25,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// last command
|
||||
cli.Command{
|
||||
Name: "last",
|
||||
Usage: "show latest build details",
|
||||
Action: buildLast,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildInfo,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch name",
|
||||
Value: "master",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// info command
|
||||
cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show build details",
|
||||
Action: buildInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildInfo,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// stop command
|
||||
cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "stop a build",
|
||||
Action: buildStop,
|
||||
},
|
||||
|
||||
// start command
|
||||
cli.Command{
|
||||
Name: "start",
|
||||
Usage: "start a build",
|
||||
Action: buildStart,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "fork",
|
||||
Usage: "fork the build",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// queue command.
|
||||
cli.Command{
|
||||
Name: "queue",
|
||||
Usage: "show build queue",
|
||||
Action: buildQueue,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildQueue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// command to display a list of recent builds.
|
||||
func buildList(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builds, err := client.BuildList(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branch := c.String("branch")
|
||||
event := c.String("event")
|
||||
status := c.String("status")
|
||||
limit := c.Int("limit")
|
||||
|
||||
var count int
|
||||
for _, build := range builds {
|
||||
if count >= limit {
|
||||
break
|
||||
}
|
||||
if branch != "" && build.Branch != branch {
|
||||
continue
|
||||
}
|
||||
if event != "" && build.Event != event {
|
||||
continue
|
||||
}
|
||||
if status != "" && build.Status != status {
|
||||
continue
|
||||
}
|
||||
tmpl.Execute(os.Stdout, build)
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to display the last build
|
||||
func buildLast(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
build, err := client.BuildLast(owner, name, c.String("branch"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, build)
|
||||
}
|
||||
|
||||
// command to display build information.
|
||||
func buildInfo(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
build, err := client.Build(owner, name, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, build)
|
||||
}
|
||||
|
||||
// command to stop a running build.
|
||||
func buildStop(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job, _ := strconv.Atoi(c.Args().Get(2))
|
||||
if job == 0 {
|
||||
job = 1
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.BuildStop(owner, name, number, job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Stopping build %s/%s#%d.%d\n", owner, name, number, job)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to re-start an existing build.
|
||||
func buildStart(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var build *drone.Build
|
||||
if c.Bool("fork") {
|
||||
build, err = client.BuildStart(owner, name, number)
|
||||
} else {
|
||||
build, err = client.BuildFork(owner, name, number)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Starting build %s/%s#%d\n", owner, name, build.Number)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to display the build queue.
|
||||
func buildQueue(c *cli.Context) error {
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builds, err := client.BuildQueue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(builds) == 0 {
|
||||
fmt.Println("there are no pending or running builds")
|
||||
return nil
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, build := range builds {
|
||||
tmpl.Execute(os.Stdout, build)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// build info template.
|
||||
var tmplBuildInfo = `Number: {{ .Number }}
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Message: {{ .Message }}
|
||||
Author: {{ .Author }}
|
||||
`
|
||||
|
||||
// build queue template.
|
||||
var tmplBuildQueue = "\x1b[33m{{ .FullName }} #{{ .Number }} \x1b[0m" + `
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
|
||||
Message: {{ .Message }}
|
||||
`
|
||||
|
||||
// build list template.
|
||||
var tmplBuildList = "\x1b[33mBuild #{{ .Number }} \x1b[0m" + `
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
|
||||
Message: {{ .Message }}
|
||||
`
|
|
@ -0,0 +1,20 @@
|
|||
package build
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// Command exports the build command set.
|
||||
var Command = cli.Command{
|
||||
Name: "build",
|
||||
Usage: "manage builds",
|
||||
Subcommands: []cli.Command{
|
||||
buildListCmd,
|
||||
buildLastCmd,
|
||||
buildLogsCmd,
|
||||
buildInfoCmd,
|
||||
buildStopCmd,
|
||||
buildStartCmd,
|
||||
buildApproveCmd,
|
||||
buildDeclineCmd,
|
||||
buildQueueCmd,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildApproveCmd = cli.Command{
|
||||
Name: "approve",
|
||||
Usage: "approve a build",
|
||||
Action: buildApprove,
|
||||
}
|
||||
|
||||
func buildApprove(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.BuildApprove(owner, name, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Approving build %s/%s#%d\n", owner, name, number)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildDeclineCmd = cli.Command{
|
||||
Name: "decline",
|
||||
Usage: "decline a build",
|
||||
Action: buildDecline,
|
||||
}
|
||||
|
||||
func buildDecline(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.BuildDecline(owner, name, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Declining build %s/%s#%d\n", owner, name, number)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildInfoCmd = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show build details",
|
||||
Action: buildInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildInfo(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buildArg := c.Args().Get(1)
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var number int
|
||||
if buildArg == "last" {
|
||||
// Fetch the build number from the last build
|
||||
build, err := client.BuildLast(owner, name, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number = build.Number
|
||||
} else {
|
||||
number, err = strconv.Atoi(buildArg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
build, err := client.Build(owner, name, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, build)
|
||||
}
|
||||
|
||||
// template for build information
|
||||
var tmplBuildInfo = `Number: {{ .Number }}
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Message: {{ .Message }}
|
||||
Author: {{ .Author }}
|
||||
`
|
|
@ -0,0 +1,51 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildLastCmd = cli.Command{
|
||||
Name: "last",
|
||||
Usage: "show latest build details",
|
||||
Action: buildLast,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildInfo,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch name",
|
||||
Value: "master",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildLast(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
build, err := client.BuildLast(owner, name, c.String("branch"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, build)
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildListCmd = cli.Command{
|
||||
Name: "list",
|
||||
Usage: "show build history",
|
||||
Action: buildList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildList,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "event",
|
||||
Usage: "event filter",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "status",
|
||||
Usage: "status filter",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "limit the list size",
|
||||
Value: 25,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildList(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builds, err := client.BuildList(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branch := c.String("branch")
|
||||
event := c.String("event")
|
||||
status := c.String("status")
|
||||
limit := c.Int("limit")
|
||||
|
||||
var count int
|
||||
for _, build := range builds {
|
||||
if count >= limit {
|
||||
break
|
||||
}
|
||||
if branch != "" && build.Branch != branch {
|
||||
continue
|
||||
}
|
||||
if event != "" && build.Event != event {
|
||||
continue
|
||||
}
|
||||
if status != "" && build.Status != status {
|
||||
continue
|
||||
}
|
||||
tmpl.Execute(os.Stdout, build)
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for build list information
|
||||
var tmplBuildList = "\x1b[33mBuild #{{ .Number }} \x1b[0m" + `
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
|
||||
Message: {{ .Message }}
|
||||
`
|
|
@ -0,0 +1,17 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildLogsCmd = cli.Command{
|
||||
Name: "logs",
|
||||
Usage: "show build logs",
|
||||
Action: buildLogs,
|
||||
}
|
||||
|
||||
func buildLogs(c *cli.Context) error {
|
||||
return fmt.Errorf("Command temporarily disabled. See https://github.com/drone/drone/issues/2005")
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildQueueCmd = cli.Command{
|
||||
Name: "queue",
|
||||
Usage: "show build queue",
|
||||
Action: buildQueue,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplBuildQueue,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildQueue(c *cli.Context) error {
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
builds, err := client.BuildQueue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(builds) == 0 {
|
||||
fmt.Println("there are no pending or running builds")
|
||||
return nil
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, build := range builds {
|
||||
tmpl.Execute(os.Stdout, build)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for build list information
|
||||
var tmplBuildQueue = "\x1b[33m{{ .FullName }} #{{ .Number }} \x1b[0m" + `
|
||||
Status: {{ .Status }}
|
||||
Event: {{ .Event }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Author: {{ .Author }} {{ if .Email }}<{{.Email}}>{{ end }}
|
||||
Message: {{ .Message }}
|
||||
`
|
|
@ -0,0 +1,70 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildStartCmd = cli.Command{
|
||||
Name: "start",
|
||||
Usage: "start a build",
|
||||
Action: buildStart,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "fork",
|
||||
Usage: "fork the build",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "param, p",
|
||||
Usage: "custom parameters to be injected into the job environment. Format: KEY=value",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildStart(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildArg := c.Args().Get(1)
|
||||
var number int
|
||||
if buildArg == "last" {
|
||||
// Fetch the build number from the last build
|
||||
build, err := client.BuildLast(owner, name, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number = build.Number
|
||||
} else {
|
||||
number, err = strconv.Atoi(buildArg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
params := internal.ParseKeyPair(c.StringSlice("param"))
|
||||
|
||||
var build *drone.Build
|
||||
if c.Bool("fork") {
|
||||
build, err = client.BuildFork(owner, name, number, params)
|
||||
} else {
|
||||
build, err = client.BuildStart(owner, name, number, params)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Starting build %s/%s#%d\n", owner, name, build.Number)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildStopCmd = cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "stop a build",
|
||||
Action: buildStop,
|
||||
}
|
||||
|
||||
func buildStop(c *cli.Context) (err error) {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
job, _ := strconv.Atoi(c.Args().Get(2))
|
||||
if job == 0 {
|
||||
job = 1
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = client.BuildStop(owner, name, number, job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Stopping build %s/%s#%d.%d\n", owner, name, number, job)
|
||||
return nil
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var deployCmd = cli.Command{
|
||||
Name: "deploy",
|
||||
Usage: "deploy code",
|
||||
Action: deploy,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplDeployInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func deploy(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
number, err := strconv.Atoi(c.Args().Get(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
build, err := client.Build(owner, name, number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if build.Event == drone.EventPull {
|
||||
return fmt.Errorf("Cannot deploy a pull request")
|
||||
}
|
||||
env := c.Args().Get(2)
|
||||
if env == "" {
|
||||
return fmt.Errorf("Please specify the target environment (ie production)")
|
||||
}
|
||||
|
||||
deploy, err := client.Deploy(owner, name, number, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, deploy)
|
||||
}
|
||||
|
||||
// template for deployment information
|
||||
var tmplDeployInfo = `Number: {{ .Number }}
|
||||
Status: {{ .Status }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Message: {{ .Message }}
|
||||
Author: {{ .Author }}
|
||||
Target: {{ .Deploy }}
|
||||
`
|
|
@ -0,0 +1,124 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Command exports the deploy command.
|
||||
var Command = cli.Command{
|
||||
Name: "deploy",
|
||||
Usage: "deploy code",
|
||||
Action: deploy,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplDeployInfo,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "branch",
|
||||
Usage: "branch filter",
|
||||
Value: "master",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "event",
|
||||
Usage: "event filter",
|
||||
Value: drone.EventPush,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "status",
|
||||
Usage: "status filter",
|
||||
Value: drone.StatusSuccess,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "param, p",
|
||||
Usage: "custom parameters to be injected into the job environment. Format: KEY=value",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func deploy(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branch := c.String("branch")
|
||||
event := c.String("event")
|
||||
status := c.String("status")
|
||||
|
||||
buildArg := c.Args().Get(1)
|
||||
var number int
|
||||
if buildArg == "last" {
|
||||
// Fetch the build number from the last build
|
||||
builds, berr := client.BuildList(owner, name)
|
||||
if berr != nil {
|
||||
return berr
|
||||
}
|
||||
for _, build := range builds {
|
||||
if branch != "" && build.Branch != branch {
|
||||
continue
|
||||
}
|
||||
if event != "" && build.Event != event {
|
||||
continue
|
||||
}
|
||||
if status != "" && build.Status != status {
|
||||
continue
|
||||
}
|
||||
if build.Number > number {
|
||||
number = build.Number
|
||||
}
|
||||
}
|
||||
if number == 0 {
|
||||
return fmt.Errorf("Cannot deploy failure build")
|
||||
}
|
||||
} else {
|
||||
number, err = strconv.Atoi(buildArg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
env := c.Args().Get(2)
|
||||
if env == "" {
|
||||
return fmt.Errorf("Please specify the target environment (ie production)")
|
||||
}
|
||||
|
||||
params := internal.ParseKeyPair(c.StringSlice("param"))
|
||||
|
||||
deploy, err := client.Deploy(owner, name, number, env, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, deploy)
|
||||
}
|
||||
|
||||
// template for deployment information
|
||||
var tmplDeployInfo = `Number: {{ .Number }}
|
||||
Status: {{ .Status }}
|
||||
Commit: {{ .Commit }}
|
||||
Branch: {{ .Branch }}
|
||||
Ref: {{ .Ref }}
|
||||
Message: {{ .Message }}
|
||||
Author: {{ .Author }}
|
||||
Target: {{ .Deploy }}
|
||||
`
|
423
drone/exec.go
423
drone/exec.go
|
@ -1,423 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone-exec/agent"
|
||||
"github.com/drone/drone-exec/build/docker"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var execCmd = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "execute a local build",
|
||||
Action: func(c *cli.Context) {
|
||||
if err := exec(c); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{
|
||||
Name: "local",
|
||||
Usage: "build from local directory",
|
||||
EnvVar: "DRONE_LOCAL",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "plugin",
|
||||
Usage: "plugin steps to enable",
|
||||
EnvVar: "DRONE_PLUGIN_ENABLE",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "secret",
|
||||
Usage: "build secrets in KEY=VALUE format",
|
||||
EnvVar: "DRONE_SECRET",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "matrix",
|
||||
Usage: "build matrix in KEY=VALUE format",
|
||||
EnvVar: "DRONE_MATRIX",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "build timeout",
|
||||
Value: time.Hour,
|
||||
EnvVar: "DRONE_TIMEOUT",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout.inactivity",
|
||||
Usage: "build timeout for inactivity",
|
||||
Value: time.Minute * 15,
|
||||
EnvVar: "DRONE_TIMEOUT_INACTIVITY",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DRONE_PLUGIN_PULL",
|
||||
Name: "pull",
|
||||
Usage: "always pull latest plugin images",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DRONE_PLUGIN_NAMESPACE",
|
||||
Name: "namespace",
|
||||
Value: "plugins",
|
||||
Usage: "default plugin image namespace",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
EnvVar: "DRONE_PLUGIN_PRIVILEGED",
|
||||
Name: "privileged",
|
||||
Usage: "plugins that require privileged mode",
|
||||
Value: &cli.StringSlice{
|
||||
"plugins/docker",
|
||||
"plugins/docker:*",
|
||||
"plguins/gcr",
|
||||
"plguins/gcr:*",
|
||||
"plugins/ecr",
|
||||
"plugins/ecr:*",
|
||||
},
|
||||
},
|
||||
|
||||
// Docker daemon flags
|
||||
|
||||
cli.StringFlag{
|
||||
EnvVar: "DOCKER_HOST",
|
||||
Name: "docker-host",
|
||||
Usage: "docker deamon address",
|
||||
Value: "unix:///var/run/docker.sock",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
EnvVar: "DOCKER_TLS_VERIFY",
|
||||
Name: "docker-tls-verify",
|
||||
Usage: "docker daemon supports tlsverify",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "DOCKER_CERT_PATH",
|
||||
Name: "docker-cert-path",
|
||||
Usage: "docker certificate directory",
|
||||
Value: "",
|
||||
},
|
||||
|
||||
//
|
||||
// Please note the below flags are mirrored in the plugin starter kit and
|
||||
// should be kept synchronized.
|
||||
// https://github.com/drone/drone-plugin-starter
|
||||
//
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "repo.fullname",
|
||||
Usage: "repository full name",
|
||||
EnvVar: "DRONE_REPO",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.owner",
|
||||
Usage: "repository owner",
|
||||
EnvVar: "DRONE_REPO_OWNER",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.name",
|
||||
Usage: "repository name",
|
||||
EnvVar: "DRONE_REPO_NAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.type",
|
||||
Value: "git",
|
||||
Usage: "repository type",
|
||||
EnvVar: "DRONE_REPO_SCM",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.link",
|
||||
Usage: "repository link",
|
||||
EnvVar: "DRONE_REPO_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.avatar",
|
||||
Usage: "repository avatar",
|
||||
EnvVar: "DRONE_REPO_AVATAR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo.branch",
|
||||
Usage: "repository default branch",
|
||||
EnvVar: "DRONE_REPO_BRANCH",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "repo.private",
|
||||
Usage: "repository is private",
|
||||
EnvVar: "DRONE_REPO_PRIVATE",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "repo.trusted",
|
||||
Usage: "repository is trusted",
|
||||
EnvVar: "DRONE_REPO_TRUSTED",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "remote.url",
|
||||
Usage: "git remote url",
|
||||
EnvVar: "DRONE_REMOTE_URL",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.sha",
|
||||
Usage: "git commit sha",
|
||||
EnvVar: "DRONE_COMMIT_SHA",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.ref",
|
||||
Value: "refs/heads/master",
|
||||
Usage: "git commit ref",
|
||||
EnvVar: "DRONE_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.branch",
|
||||
Value: "master",
|
||||
Usage: "git commit branch",
|
||||
EnvVar: "DRONE_COMMIT_BRANCH",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.message",
|
||||
Usage: "git commit message",
|
||||
EnvVar: "DRONE_COMMIT_MESSAGE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.link",
|
||||
Usage: "git commit link",
|
||||
EnvVar: "DRONE_COMMIT_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.author.name",
|
||||
Usage: "git author name",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.author.email",
|
||||
Usage: "git author email",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit.author.avatar",
|
||||
Usage: "git author avatar",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build.event",
|
||||
Value: "push",
|
||||
Usage: "build event",
|
||||
EnvVar: "DRONE_BUILD_EVENT",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "build.number",
|
||||
Usage: "build number",
|
||||
EnvVar: "DRONE_BUILD_NUMBER",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "build.created",
|
||||
Usage: "build created",
|
||||
EnvVar: "DRONE_BUILD_CREATED",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "build.started",
|
||||
Usage: "build started",
|
||||
EnvVar: "DRONE_BUILD_STARTED",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "build.finished",
|
||||
Usage: "build finished",
|
||||
EnvVar: "DRONE_BUILD_FINISHED",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build.status",
|
||||
Usage: "build status",
|
||||
Value: "success",
|
||||
EnvVar: "DRONE_BUILD_STATUS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build.link",
|
||||
Usage: "build link",
|
||||
EnvVar: "DRONE_BUILD_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build.deploy",
|
||||
Usage: "build deployment target",
|
||||
EnvVar: "DRONE_DEPLOY_TO",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "yaml.verified",
|
||||
Usage: "build yaml is verified",
|
||||
EnvVar: "DRONE_YAML_VERIFIED",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "yaml.signed",
|
||||
Usage: "build yaml is signed",
|
||||
EnvVar: "DRONE_YAML_SIGNED",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "prev.build.number",
|
||||
Usage: "previous build number",
|
||||
EnvVar: "DRONE_PREV_BUILD_NUMBER",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev.build.status",
|
||||
Usage: "previous build status",
|
||||
EnvVar: "DRONE_PREV_BUILD_STATUS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev.commit.sha",
|
||||
Usage: "previous build sha",
|
||||
EnvVar: "DRONE_PREV_COMMIT_SHA",
|
||||
},
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "netrc.username",
|
||||
Usage: "previous build sha",
|
||||
EnvVar: "DRONE_NETRC_USERNAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "netrc.password",
|
||||
Usage: "previous build sha",
|
||||
EnvVar: "DRONE_NETRC_PASSWORD",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "netrc.machine",
|
||||
Usage: "previous build sha",
|
||||
EnvVar: "DRONE_NETRC_MACHINE",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func exec(c *cli.Context) error {
|
||||
sigterm := make(chan os.Signal, 1)
|
||||
cancelc := make(chan bool, 1)
|
||||
signal.Notify(sigterm, os.Interrupt)
|
||||
go func() {
|
||||
<-sigterm
|
||||
cancelc <- true
|
||||
}()
|
||||
|
||||
path := c.Args().First()
|
||||
if path == "" {
|
||||
path = ".drone.yml"
|
||||
}
|
||||
path, _ = filepath.Abs(path)
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
engine, err := docker.New(
|
||||
c.String("docker-host"),
|
||||
c.String("docker-cert-path"),
|
||||
c.Bool("docker-tls-verify"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agent := agent.Agent{
|
||||
Update: agent.NoopUpdateFunc,
|
||||
Logger: agent.TermLoggerFunc,
|
||||
Engine: engine,
|
||||
Timeout: c.Duration("timeout.inactivity"),
|
||||
Platform: "linux/amd64",
|
||||
Namespace: c.String("namespace"),
|
||||
Disable: c.StringSlice("plugin"),
|
||||
Escalate: c.StringSlice("privileged"),
|
||||
Netrc: []string{},
|
||||
Local: dir,
|
||||
Pull: c.Bool("pull"),
|
||||
}
|
||||
|
||||
payload := drone.Payload{
|
||||
Yaml: string(file),
|
||||
Repo: &drone.Repo{
|
||||
FullName: c.String("repo.fullname"),
|
||||
Owner: c.String("repo.owner"),
|
||||
Name: c.String("repo.name"),
|
||||
Kind: c.String("repo.type"),
|
||||
Link: c.String("repo.link"),
|
||||
Branch: c.String("repo.branch"),
|
||||
Avatar: c.String("repo.avatar"),
|
||||
Timeout: int64(c.Duration("timeout").Minutes()),
|
||||
IsPrivate: c.Bool("repo.private"),
|
||||
IsTrusted: c.Bool("repo.trusted"),
|
||||
Clone: c.String("remote.url"),
|
||||
},
|
||||
System: &drone.System{
|
||||
Link: c.GlobalString("server"),
|
||||
},
|
||||
Secrets: getSecrets(c),
|
||||
Netrc: &drone.Netrc{
|
||||
Login: c.String("netrc.username"),
|
||||
Password: c.String("netrc.password"),
|
||||
Machine: c.String("netrc.machine"),
|
||||
},
|
||||
Build: &drone.Build{
|
||||
Commit: c.String("commit.sha"),
|
||||
Branch: c.String("commit.branch"),
|
||||
Ref: c.String("commit.ref"),
|
||||
Link: c.String("commit.link"),
|
||||
Message: c.String("commit.message"),
|
||||
Author: c.String("commit.author.name"),
|
||||
Email: c.String("commit.author.email"),
|
||||
Avatar: c.String("commit.author.avatar"),
|
||||
Number: c.Int("build.number"),
|
||||
Event: c.String("build.event"),
|
||||
Deploy: c.String("build.deploy"),
|
||||
Verified: c.BoolT("yaml.verified"),
|
||||
Signed: c.BoolT("yaml.signed"),
|
||||
},
|
||||
BuildLast: &drone.Build{
|
||||
Number: c.Int("prev.build.number"),
|
||||
Status: c.String("prev.build.status"),
|
||||
Commit: c.String("prev.commit.sha"),
|
||||
},
|
||||
Job: &drone.Job{
|
||||
Environment: getMatrix(c),
|
||||
},
|
||||
}
|
||||
|
||||
return agent.Run(&payload, cancelc)
|
||||
}
|
||||
|
||||
// helper function to retrieve matrix variables.
|
||||
func getMatrix(c *cli.Context) map[string]string {
|
||||
envs := map[string]string{}
|
||||
for _, s := range c.StringSlice("matrix") {
|
||||
parts := strings.SplitN(s, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
k := parts[0]
|
||||
v := parts[1]
|
||||
envs[k] = v
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
// helper function to retrieve secret variables.
|
||||
func getSecrets(c *cli.Context) []*drone.Secret {
|
||||
var secrets []*drone.Secret
|
||||
for _, s := range c.StringSlice("secret") {
|
||||
parts := strings.SplitN(s, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
secret := &drone.Secret{
|
||||
Name: parts[0],
|
||||
Value: parts[1],
|
||||
Events: []string{
|
||||
drone.EventPull,
|
||||
drone.EventPush,
|
||||
drone.EventTag,
|
||||
drone.EventDeploy,
|
||||
},
|
||||
Images: []string{"*"},
|
||||
}
|
||||
secrets = append(secrets, secret)
|
||||
}
|
||||
return secrets
|
||||
}
|
|
@ -0,0 +1,456 @@
|
|||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cncd/pipeline/pipeline"
|
||||
"github.com/cncd/pipeline/pipeline/backend"
|
||||
"github.com/cncd/pipeline/pipeline/backend/docker"
|
||||
"github.com/cncd/pipeline/pipeline/frontend"
|
||||
"github.com/cncd/pipeline/pipeline/frontend/yaml"
|
||||
"github.com/cncd/pipeline/pipeline/frontend/yaml/compiler"
|
||||
"github.com/cncd/pipeline/pipeline/frontend/yaml/linter"
|
||||
"github.com/cncd/pipeline/pipeline/interrupt"
|
||||
"github.com/cncd/pipeline/pipeline/multipart"
|
||||
"github.com/drone/envsubst"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Command exports the exec command.
|
||||
var Command = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "execute a local build",
|
||||
Action: func(c *cli.Context) {
|
||||
if err := exec(c); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
},
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolTFlag{
|
||||
Name: "local",
|
||||
Usage: "build from local directory",
|
||||
EnvVar: "DRONE_LOCAL",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "build timeout",
|
||||
Value: time.Hour,
|
||||
EnvVar: "DRONE_TIMEOUT",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "volumes",
|
||||
Usage: "build volumes",
|
||||
EnvVar: "DRONE_VOLUMES",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "network",
|
||||
Usage: "external networks",
|
||||
EnvVar: "DRONE_NETWORKS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prefix",
|
||||
Value: "drone",
|
||||
Usage: "prefix containers created by drone",
|
||||
EnvVar: "DRONE_DOCKER_PREFIX",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "privileged",
|
||||
Usage: "privileged plugins",
|
||||
Value: &cli.StringSlice{
|
||||
"plugins/docker",
|
||||
"plugins/gcr",
|
||||
"plugins/ecr",
|
||||
},
|
||||
},
|
||||
|
||||
//
|
||||
// Please note the below flags are mirrored in the pipec and
|
||||
// should be kept synchronized. Do not edit directly
|
||||
// https://github.com/cncd/pipeline/pipec
|
||||
//
|
||||
|
||||
//
|
||||
// workspace default
|
||||
//
|
||||
cli.StringFlag{
|
||||
Name: "workspace-base",
|
||||
Value: "/pipeline",
|
||||
EnvVar: "DRONE_WORKSPACE_BASE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "workspace-path",
|
||||
Value: "src",
|
||||
EnvVar: "DRONE_WORKSPACE_PATH",
|
||||
},
|
||||
//
|
||||
// netrc parameters
|
||||
//
|
||||
cli.StringFlag{
|
||||
Name: "netrc-username",
|
||||
EnvVar: "DRONE_NETRC_USERNAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "netrc-password",
|
||||
EnvVar: "DRONE_NETRC_PASSWORD",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "netrc-machine",
|
||||
EnvVar: "DRONE_NETRC_MACHINE",
|
||||
},
|
||||
//
|
||||
// metadata parameters
|
||||
//
|
||||
cli.StringFlag{
|
||||
Name: "system-arch",
|
||||
Value: "linux/amd64",
|
||||
EnvVar: "DRONE_SYSTEM_ARCH",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "system-name",
|
||||
Value: "pipec",
|
||||
EnvVar: "DRONE_SYSTEM_NAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "system-link",
|
||||
Value: "https://github.com/cncd/pipec",
|
||||
EnvVar: "DRONE_SYSTEM_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo-name",
|
||||
EnvVar: "DRONE_REPO_NAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo-link",
|
||||
EnvVar: "DRONE_REPO_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo-remote-url",
|
||||
EnvVar: "DRONE_REPO_REMOTE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo-private",
|
||||
EnvVar: "DRONE_REPO_PRIVATE",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "build-number",
|
||||
EnvVar: "DRONE_BUILD_NUMBER",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parent-build-number",
|
||||
EnvVar: "DRONE_PARENT_BUILD_NUMBER",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "build-created",
|
||||
EnvVar: "DRONE_BUILD_CREATED",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "build-started",
|
||||
EnvVar: "DRONE_BUILD_STARTED",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "build-finished",
|
||||
EnvVar: "DRONE_BUILD_FINISHED",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build-status",
|
||||
EnvVar: "DRONE_BUILD_STATUS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build-event",
|
||||
EnvVar: "DRONE_BUILD_EVENT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build-link",
|
||||
EnvVar: "DRONE_BUILD_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "build-target",
|
||||
EnvVar: "DRONE_BUILD_TARGET",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-sha",
|
||||
EnvVar: "DRONE_COMMIT_SHA",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-ref",
|
||||
EnvVar: "DRONE_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-refspec",
|
||||
EnvVar: "DRONE_COMMIT_REFSPEC",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-branch",
|
||||
EnvVar: "DRONE_COMMIT_BRANCH",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-message",
|
||||
EnvVar: "DRONE_COMMIT_MESSAGE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-author-name",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR_NAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-author-avatar",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR_AVATAR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "commit-author-email",
|
||||
EnvVar: "DRONE_COMMIT_AUTHOR_EMAIL",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "prev-build-number",
|
||||
EnvVar: "DRONE_PREV_BUILD_NUMBER",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "prev-build-created",
|
||||
EnvVar: "DRONE_PREV_BUILD_CREATED",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "prev-build-started",
|
||||
EnvVar: "DRONE_PREV_BUILD_STARTED",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "prev-build-finished",
|
||||
EnvVar: "DRONE_PREV_BUILD_FINISHED",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-build-status",
|
||||
EnvVar: "DRONE_PREV_BUILD_STATUS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-build-event",
|
||||
EnvVar: "DRONE_PREV_BUILD_EVENT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-build-link",
|
||||
EnvVar: "DRONE_PREV_BUILD_LINK",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-sha",
|
||||
EnvVar: "DRONE_PREV_COMMIT_SHA",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-ref",
|
||||
EnvVar: "DRONE_PREV_COMMIT_REF",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-refspec",
|
||||
EnvVar: "DRONE_PREV_COMMIT_REFSPEC",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-branch",
|
||||
EnvVar: "DRONE_PREV_COMMIT_BRANCH",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-message",
|
||||
EnvVar: "DRONE_PREV_COMMIT_MESSAGE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-author-name",
|
||||
EnvVar: "DRONE_PREV_COMMIT_AUTHOR_NAME",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-author-avatar",
|
||||
EnvVar: "DRONE_PREV_COMMIT_AUTHOR_AVATAR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prev-commit-author-email",
|
||||
EnvVar: "DRONE_PREV_COMMIT_AUTHOR_EMAIL",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "job-number",
|
||||
EnvVar: "DRONE_JOB_NUMBER",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func exec(c *cli.Context) error {
|
||||
file := c.Args().First()
|
||||
if file == "" {
|
||||
file = ".drone.yml"
|
||||
}
|
||||
|
||||
metadata := metadataFromContext(c)
|
||||
environ := metadata.Environ()
|
||||
secrets := []compiler.Secret{}
|
||||
for k, v := range metadata.EnvironDrone() {
|
||||
environ[k] = v
|
||||
}
|
||||
for _, env := range os.Environ() {
|
||||
k := strings.Split(env, "=")[0]
|
||||
v := strings.Split(env, "=")[1]
|
||||
environ[k] = v
|
||||
secrets = append(secrets, compiler.Secret{
|
||||
Name: k,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
tmpl, err := envsubst.ParseFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
confstr, err := tmpl.Execute(func(name string) string {
|
||||
return environ[name]
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf, err := yaml.ParseString(confstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// configure volumes for local execution
|
||||
volumes := c.StringSlice("volumes")
|
||||
if c.Bool("local") {
|
||||
var (
|
||||
workspaceBase = conf.Workspace.Base
|
||||
workspacePath = conf.Workspace.Path
|
||||
)
|
||||
if workspaceBase == "" {
|
||||
workspaceBase = c.String("workspace-base")
|
||||
}
|
||||
if workspacePath == "" {
|
||||
workspacePath = c.String("workspace-path")
|
||||
}
|
||||
dir, _ := filepath.Abs(filepath.Dir(file))
|
||||
volumes = append(volumes, dir+":"+path.Join(workspaceBase, workspacePath))
|
||||
}
|
||||
|
||||
// lint the yaml file
|
||||
if lerr := linter.New(linter.WithTrusted(true)).Lint(conf); lerr != nil {
|
||||
return lerr
|
||||
}
|
||||
|
||||
// compiles the yaml file
|
||||
compiled := compiler.New(
|
||||
compiler.WithEscalated(
|
||||
c.StringSlice("privileged")...,
|
||||
),
|
||||
compiler.WithVolumes(volumes...),
|
||||
compiler.WithWorkspace(
|
||||
c.String("workspace-base"),
|
||||
c.String("workspace-path"),
|
||||
),
|
||||
compiler.WithNetworks(
|
||||
c.StringSlice("network")...,
|
||||
),
|
||||
compiler.WithPrefix(
|
||||
c.String("prefix"),
|
||||
),
|
||||
compiler.WithProxy(),
|
||||
compiler.WithLocal(
|
||||
c.Bool("local"),
|
||||
),
|
||||
compiler.WithNetrc(
|
||||
c.String("netrc-username"),
|
||||
c.String("netrc-password"),
|
||||
c.String("netrc-machine"),
|
||||
),
|
||||
compiler.WithMetadata(metadata),
|
||||
compiler.WithSecret(secrets...),
|
||||
).Compile(conf)
|
||||
|
||||
engine, err := docker.NewEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
|
||||
defer cancel()
|
||||
ctx = interrupt.WithContext(ctx)
|
||||
|
||||
return pipeline.New(compiled,
|
||||
pipeline.WithContext(ctx),
|
||||
pipeline.WithLogger(defaultLogger),
|
||||
pipeline.WithTracer(pipeline.DefaultTracer),
|
||||
pipeline.WithLogger(defaultLogger),
|
||||
pipeline.WithEngine(engine),
|
||||
).Run()
|
||||
}
|
||||
|
||||
// return the metadata from the cli context.
|
||||
func metadataFromContext(c *cli.Context) frontend.Metadata {
|
||||
return frontend.Metadata{
|
||||
Repo: frontend.Repo{
|
||||
Name: c.String("repo-name"),
|
||||
Link: c.String("repo-link"),
|
||||
Remote: c.String("repo-remote-url"),
|
||||
Private: c.Bool("repo-private"),
|
||||
},
|
||||
Curr: frontend.Build{
|
||||
Number: c.Int("build-number"),
|
||||
Parent: c.Int("parent-build-number"),
|
||||
Created: c.Int64("build-created"),
|
||||
Started: c.Int64("build-started"),
|
||||
Finished: c.Int64("build-finished"),
|
||||
Status: c.String("build-status"),
|
||||
Event: c.String("build-event"),
|
||||
Link: c.String("build-link"),
|
||||
Target: c.String("build-target"),
|
||||
Commit: frontend.Commit{
|
||||
Sha: c.String("commit-sha"),
|
||||
Ref: c.String("commit-ref"),
|
||||
Refspec: c.String("commit-refspec"),
|
||||
Branch: c.String("commit-branch"),
|
||||
Message: c.String("commit-message"),
|
||||
Author: frontend.Author{
|
||||
Name: c.String("commit-author-name"),
|
||||
Email: c.String("commit-author-email"),
|
||||
Avatar: c.String("commit-author-avatar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Prev: frontend.Build{
|
||||
Number: c.Int("prev-build-number"),
|
||||
Created: c.Int64("prev-build-created"),
|
||||
Started: c.Int64("prev-build-started"),
|
||||
Finished: c.Int64("prev-build-finished"),
|
||||
Status: c.String("prev-build-status"),
|
||||
Event: c.String("prev-build-event"),
|
||||
Link: c.String("prev-build-link"),
|
||||
Commit: frontend.Commit{
|
||||
Sha: c.String("prev-commit-sha"),
|
||||
Ref: c.String("prev-commit-ref"),
|
||||
Refspec: c.String("prev-commit-refspec"),
|
||||
Branch: c.String("prev-commit-branch"),
|
||||
Message: c.String("prev-commit-message"),
|
||||
Author: frontend.Author{
|
||||
Name: c.String("prev-commit-author-name"),
|
||||
Email: c.String("prev-commit-author-email"),
|
||||
Avatar: c.String("prev-commit-author-avatar"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Job: frontend.Job{
|
||||
Number: c.Int("job-number"),
|
||||
},
|
||||
Sys: frontend.System{
|
||||
Name: c.String("system-name"),
|
||||
Link: c.String("system-link"),
|
||||
Arch: c.String("system-arch"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var defaultLogger = pipeline.LogFunc(func(proc *backend.Step, rc multipart.Reader) error {
|
||||
part, err := rc.NextPart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(os.Stderr, part)
|
||||
return nil
|
||||
})
|
|
@ -1,27 +1,31 @@
|
|||
package main
|
||||
package info
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var infoCmd = cli.Command{
|
||||
// Command exports the info command.
|
||||
var Command = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display session info",
|
||||
Usage: "show information about the current user",
|
||||
Action: info,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplUserInfo,
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplInfo,
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func info(c *cli.Context) error {
|
||||
client, err := newClient(c)
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/jackspirou/syscerts"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/proxy"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
// NewClient returns a new client from the CLI context.
|
||||
func NewClient(c *cli.Context) (drone.Client, error) {
|
||||
var (
|
||||
skip = c.GlobalBool("skip-verify")
|
||||
socks = c.GlobalString("socks-proxy")
|
||||
socksoff = c.GlobalBool("socks-proxy-off")
|
||||
token = c.GlobalString("token")
|
||||
server = c.GlobalString("server")
|
||||
)
|
||||
server = strings.TrimRight(server, "/")
|
||||
|
||||
// if no server url is provided we can default
|
||||
// to the hosted Drone service.
|
||||
if len(server) == 0 {
|
||||
return nil, fmt.Errorf("Error: you must provide the Drone server address.")
|
||||
}
|
||||
if len(token) == 0 {
|
||||
return nil, fmt.Errorf("Error: you must provide your Drone access token.")
|
||||
}
|
||||
|
||||
// attempt to find system CA certs
|
||||
certs := syscerts.SystemRootsPool()
|
||||
tlsConfig := &tls.Config{
|
||||
RootCAs: certs,
|
||||
InsecureSkipVerify: skip,
|
||||
}
|
||||
|
||||
config := new(oauth2.Config)
|
||||
auther := config.Client(
|
||||
oauth2.NoContext,
|
||||
&oauth2.Token{
|
||||
AccessToken: token,
|
||||
},
|
||||
)
|
||||
|
||||
trans, _ := auther.Transport.(*oauth2.Transport)
|
||||
|
||||
if len(socks) != 0 && !socksoff {
|
||||
dialer, err := proxy.SOCKS5("tcp", socks, nil, proxy.Direct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trans.Base = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: dialer.Dial,
|
||||
}
|
||||
} else {
|
||||
trans.Base = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
}
|
||||
|
||||
return drone.NewClient(server, auther), nil
|
||||
}
|
||||
|
||||
// ParseRepo parses the repository owner and name from a string.
|
||||
func ParseRepo(str string) (user, repo string, err error) {
|
||||
var parts = strings.Split(str, "/")
|
||||
if len(parts) != 2 {
|
||||
err = fmt.Errorf("Error: Invalid or missing repository. eg octocat/hello-world.")
|
||||
return
|
||||
}
|
||||
user = parts[0]
|
||||
repo = parts[1]
|
||||
return
|
||||
}
|
||||
|
||||
// ParseKeyPair parses a key=value pair.
|
||||
func ParseKeyPair(p []string) map[string]string {
|
||||
params := map[string]string{}
|
||||
for _, i := range p {
|
||||
parts := strings.Split(i, "=")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
params[parts[0]] = parts[1]
|
||||
}
|
||||
return params
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package internal
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseKeyPair(t *testing.T) {
|
||||
s := []string{"FOO=bar", "BAR=", "INVALID"}
|
||||
p := ParseKeyPair(s)
|
||||
if p["FOO"] != "bar" {
|
||||
t.Errorf("Wanted %q, got %q.", "bar", p["FOO"])
|
||||
}
|
||||
if _, exists := p["BAR"]; !exists {
|
||||
t.Error("Missing a key with no value. Keys with empty values are also valid.")
|
||||
}
|
||||
if _, exists := p["INVALID"]; exists {
|
||||
t.Error("Keys without an equal sign suffix are invalid.")
|
||||
}
|
||||
}
|
|
@ -1,18 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/drone/drone-cli/drone/build"
|
||||
"github.com/drone/drone-cli/drone/deploy"
|
||||
"github.com/drone/drone-cli/drone/exec"
|
||||
"github.com/drone/drone-cli/drone/info"
|
||||
"github.com/drone/drone-cli/drone/registry"
|
||||
"github.com/drone/drone-cli/drone/repo"
|
||||
"github.com/drone/drone-cli/drone/secret"
|
||||
"github.com/drone/drone-cli/drone/user"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// drone version number
|
||||
var version string
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "drone"
|
||||
app.Version = "0.5"
|
||||
app.Version = version
|
||||
app.Usage = "command line utility"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
|
@ -25,20 +36,38 @@ func main() {
|
|||
Usage: "server location",
|
||||
EnvVar: "DRONE_SERVER",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-verify",
|
||||
Usage: "skip ssl verfification",
|
||||
EnvVar: "DRONE_SKIP_VERIFY",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "socks-proxy",
|
||||
Usage: "socks proxy address",
|
||||
EnvVar: "SOCKS_PROXY",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "socks-proxy-off",
|
||||
Usage: "socks proxy ignored",
|
||||
EnvVar: "SOCKS_PROXY_OFF",
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
agentCmd,
|
||||
buildCmd,
|
||||
deployCmd,
|
||||
execCmd,
|
||||
infoCmd,
|
||||
secretCmd,
|
||||
signCmd,
|
||||
repoCmd,
|
||||
userCmd,
|
||||
build.Command,
|
||||
deploy.Command,
|
||||
exec.Command,
|
||||
info.Command,
|
||||
registry.Command,
|
||||
secret.Command,
|
||||
repo.Command,
|
||||
user.Command,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package registry
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// Command exports the registry command set.
|
||||
var Command = cli.Command{
|
||||
Name: "registry",
|
||||
Usage: "manage registries",
|
||||
Subcommands: []cli.Command{
|
||||
registryCreateCmd,
|
||||
registryDeleteCmd,
|
||||
registryUpdateCmd,
|
||||
registryInfoCmd,
|
||||
registryListCmd,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var registryCreateCmd = cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a registry",
|
||||
Action: registryCreate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hostname",
|
||||
Usage: "registry hostname",
|
||||
Value: "docker.io",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username",
|
||||
Usage: "registry username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "registry password",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func registryCreate(c *cli.Context) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
username = c.String("username")
|
||||
password = c.String("password")
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry := &drone.Registry{
|
||||
Address: hostname,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if strings.HasPrefix(registry.Password, "@") {
|
||||
path := strings.TrimPrefix(registry.Password, "@")
|
||||
out, ferr := ioutil.ReadFile(path)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
registry.Password = string(out)
|
||||
}
|
||||
_, err = client.RegistryCreate(owner, name, registry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var registryInfoCmd = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display registry info",
|
||||
Action: registryInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hostname",
|
||||
Usage: "registry hostname",
|
||||
Value: "docker.io",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRegistryList,
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func registryInfo(c *cli.Context) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
reponame = c.String("repository")
|
||||
format = c.String("format") + "\n"
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry, err := client.Registry(owner, name, hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl, err := template.New("_").Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, registry)
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var registryListCmd = cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list regitries",
|
||||
Action: registryList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRegistryList,
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func registryList(c *cli.Context) error {
|
||||
var (
|
||||
format = c.String("format") + "\n"
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := client.RegistryList(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl, err := template.New("_").Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, registry := range list {
|
||||
tmpl.Execute(os.Stdout, registry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for build list information
|
||||
var tmplRegistryList = "\x1b[33m{{ .Address }} \x1b[0m" + `
|
||||
Username: {{ .Username }}
|
||||
Email: {{ .Email }}
|
||||
`
|
|
@ -0,0 +1,43 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var registryDeleteCmd = cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a registry",
|
||||
Action: registryDelete,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hostname",
|
||||
Usage: "registry hostname",
|
||||
Value: "docker.io",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func registryDelete(c *cli.Context) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.RegistryDelete(owner, name, hostname)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var registryUpdateCmd = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update a registry",
|
||||
Action: registryUpdate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hostname",
|
||||
Usage: "registry hostname",
|
||||
Value: "docker.io",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username",
|
||||
Usage: "registry username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "registry password",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func registryUpdate(c *cli.Context) error {
|
||||
var (
|
||||
hostname = c.String("hostname")
|
||||
username = c.String("username")
|
||||
password = c.String("password")
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registry := &drone.Registry{
|
||||
Address: hostname,
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if strings.HasPrefix(registry.Password, "@") {
|
||||
path := strings.TrimPrefix(registry.Password, "@")
|
||||
out, ferr := ioutil.ReadFile(path)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
registry.Password = string(out)
|
||||
}
|
||||
_, err = client.RegistryUpdate(owner, name, registry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
165
drone/repo.go
165
drone/repo.go
|
@ -1,165 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var repoCmd = cli.Command{
|
||||
Name: "repo",
|
||||
Usage: "manage repositories",
|
||||
Subcommands: []cli.Command{
|
||||
|
||||
// list command
|
||||
cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list all repos",
|
||||
Action: repoList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRepoList,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "filter by organization",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// info command
|
||||
cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show repository details",
|
||||
Action: repoInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRepoInfo,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// add command
|
||||
cli.Command{
|
||||
Name: "add",
|
||||
Usage: "add a repository",
|
||||
Action: repoAdd,
|
||||
},
|
||||
|
||||
// remove command
|
||||
cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a repository",
|
||||
Action: repoRemove,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// command to fetch and return repository information.
|
||||
func repoInfo(c *cli.Context) error {
|
||||
arg := c.Args().First()
|
||||
owner, name, err := parseRepo(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := client.Repo(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, repo)
|
||||
}
|
||||
|
||||
// command to add a repository.
|
||||
func repoAdd(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.RepoPost(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully activated repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to remove a repository from drone.
|
||||
func repoRemove(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.RepoDel(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully removed repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to list user repositories.
|
||||
func repoList(c *cli.Context) error {
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repos, err := client.RepoList()
|
||||
if err != nil || len(repos) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
org := c.String("org")
|
||||
for _, repo := range repos {
|
||||
if org != "" && org != repo.Owner {
|
||||
continue
|
||||
}
|
||||
tmpl.Execute(os.Stdout, repo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// repository info template.
|
||||
var tmplRepoInfo = `Owner: {{ .Owner }}
|
||||
Repo: {{ .Name }}
|
||||
Type: {{ .Kind }}
|
||||
Private: {{ .IsPrivate }}
|
||||
Remote: {{ .Clone }}
|
||||
`
|
||||
|
||||
// repository list template.
|
||||
var tmplRepoList = `{{ .FullName }}`
|
|
@ -0,0 +1,18 @@
|
|||
package repo
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// Command exports the repository command.
|
||||
var Command = cli.Command{
|
||||
Name: "repo",
|
||||
Usage: "manage repositories",
|
||||
Subcommands: []cli.Command{
|
||||
repoListCmd,
|
||||
repoInfoCmd,
|
||||
repoAddCmd,
|
||||
repoUpdateCmd,
|
||||
repoRemoveCmd,
|
||||
repoRepairCmd,
|
||||
repoChownCmd,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoAddCmd = cli.Command{
|
||||
Name: "add",
|
||||
Usage: "add a repository",
|
||||
Action: repoAdd,
|
||||
}
|
||||
|
||||
func repoAdd(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.RepoPost(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully activated repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoChownCmd = cli.Command{
|
||||
Name: "chown",
|
||||
Usage: "assume ownership of a repository",
|
||||
Action: repoChown,
|
||||
}
|
||||
|
||||
func repoChown(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.RepoChown(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully assumed ownership of repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoInfoCmd = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show repository details",
|
||||
Action: repoInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRepoInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func repoInfo(c *cli.Context) error {
|
||||
arg := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := client.Repo(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, repo)
|
||||
}
|
||||
|
||||
// template for repo information
|
||||
var tmplRepoInfo = `Owner: {{ .Owner }}
|
||||
Repo: {{ .Name }}
|
||||
Type: {{ .Kind }}
|
||||
Config: {{ .Config }}
|
||||
Private: {{ .IsPrivate }}
|
||||
Trusted: {{ .IsTrusted }}
|
||||
Gated: {{ .IsGated }}
|
||||
Remote: {{ .Clone }}
|
||||
`
|
|
@ -0,0 +1,55 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoListCmd = cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list all repos",
|
||||
Action: repoList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplRepoList,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "org",
|
||||
Usage: "filter by organization",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func repoList(c *cli.Context) error {
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repos, err := client.RepoList()
|
||||
if err != nil || len(repos) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
org := c.String("org")
|
||||
for _, repo := range repos {
|
||||
if org != "" && org != repo.Owner {
|
||||
continue
|
||||
}
|
||||
tmpl.Execute(os.Stdout, repo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for repository list items
|
||||
var tmplRepoList = `{{ .FullName }}`
|
|
@ -0,0 +1,25 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoRepairCmd = cli.Command{
|
||||
Name: "repair",
|
||||
Usage: "repair repository webhooks",
|
||||
Action: repoRepair,
|
||||
}
|
||||
|
||||
func repoRepair(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.RepoRepair(owner, name)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoRemoveCmd = cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a repository",
|
||||
Action: repoRemove,
|
||||
}
|
||||
|
||||
func repoRemove(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.RepoDel(owner, name); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully removed repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var repoUpdateCmd = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update a repository",
|
||||
Action: repoUpdate,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "trusted",
|
||||
Usage: "repository is trusted",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "gated",
|
||||
Usage: "repository is gated",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "repository timeout",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "repository configuration path (e.g. .drone.yml)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func repoUpdate(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := internal.ParseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
config = c.String("config")
|
||||
timeout = c.Duration("timeout")
|
||||
trusted = c.Bool("trusted")
|
||||
gated = c.Bool("gated")
|
||||
)
|
||||
|
||||
patch := new(drone.RepoPatch)
|
||||
if c.IsSet("trusted") {
|
||||
patch.IsTrusted = &trusted
|
||||
}
|
||||
if c.IsSet("gated") {
|
||||
patch.IsGated = &gated
|
||||
}
|
||||
if c.IsSet("timeout") {
|
||||
v := int64(timeout / time.Minute)
|
||||
patch.Timeout = &v
|
||||
}
|
||||
if c.IsSet("config") {
|
||||
patch.Config = &config
|
||||
}
|
||||
|
||||
if _, err := client.RepoPatch(owner, name, patch); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully updated repository %s/%s\n", owner, name)
|
||||
return nil
|
||||
}
|
109
drone/secret.go
109
drone/secret.go
|
@ -1,109 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var secretCmd = cli.Command{
|
||||
Name: "secret",
|
||||
Usage: "manage secrets",
|
||||
Subcommands: []cli.Command{
|
||||
cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a secret",
|
||||
ArgsUsage: "[repo] [key] [value]",
|
||||
Action: secretAdd,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "inject the secret for these event types",
|
||||
Value: &cli.StringSlice{
|
||||
drone.EventPush,
|
||||
drone.EventTag,
|
||||
drone.EventDeploy,
|
||||
},
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "inject the secret for these image types",
|
||||
Value: &cli.StringSlice{},
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "input",
|
||||
Usage: "input secret value from a file",
|
||||
},
|
||||
},
|
||||
},
|
||||
cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a secret",
|
||||
Action: secretRemove,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// command to add a repository secret.
|
||||
func secretAdd(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tail := c.Args().Tail()
|
||||
if len(tail) != 2 {
|
||||
cli.ShowSubcommandHelp(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
secret := &drone.Secret{
|
||||
Name: tail[0],
|
||||
Value: tail[1],
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
|
||||
if len(secret.Images) == 0 {
|
||||
return fmt.Errorf("Please specify the --image parameter")
|
||||
}
|
||||
|
||||
// TODO(bradrydzewski) below we use an @ sybmol to denote that the secret
|
||||
// value should be loaded from a file (inspired by curl). I'd prefer to use
|
||||
// a --input flag to explicitly specify a filepath instead.
|
||||
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := secret.Value[1:]
|
||||
out, ferr := ioutil.ReadFile(path)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.SecretPost(owner, name, secret)
|
||||
}
|
||||
|
||||
// command to remove a repository secret.
|
||||
func secretRemove(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret := c.Args().Get(1)
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.SecretDel(owner, name, secret)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package secret
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// Command exports the secret command.
|
||||
var Command = cli.Command{
|
||||
Name: "secret",
|
||||
Usage: "manage secrets",
|
||||
Subcommands: []cli.Command{
|
||||
secretCreateCmd,
|
||||
secretDeleteCmd,
|
||||
secretUpdateCmd,
|
||||
secretInfoCmd,
|
||||
secretListCmd,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var secretCreateCmd = cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a secret",
|
||||
Action: secretCreate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "secret limited to these events",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "secret limited to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretCreate(c *cli.Context) error {
|
||||
reponame := c.String("repository")
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret := &drone.Secret{
|
||||
Name: c.String("name"),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("event"),
|
||||
}
|
||||
if len(secret.Events) == 0 {
|
||||
secret.Events = defaultSecretEvents
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, ferr := ioutil.ReadFile(path)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
_, err = client.SecretCreate(owner, name, secret)
|
||||
return err
|
||||
}
|
||||
|
||||
var defaultSecretEvents = []string{
|
||||
drone.EventPush,
|
||||
drone.EventTag,
|
||||
drone.EventDeploy,
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var secretInfoCmd = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "display secret info",
|
||||
Action: secretInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplSecretList,
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretInfo(c *cli.Context) error {
|
||||
var (
|
||||
secretName = c.String("name")
|
||||
repoName = c.String("repository")
|
||||
format = c.String("format") + "\n"
|
||||
)
|
||||
if repoName == "" {
|
||||
repoName = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(repoName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret, err := client.Secret(owner, name, secretName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, secret)
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var secretListCmd = cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list secrets",
|
||||
Action: secretList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplSecretList,
|
||||
Hidden: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretList(c *cli.Context) error {
|
||||
var (
|
||||
format = c.String("format") + "\n"
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := client.SecretList(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl, err := template.New("_").Funcs(secretFuncMap).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, registry := range list {
|
||||
tmpl.Execute(os.Stdout, registry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for secret list items
|
||||
var tmplSecretList = "\x1b[33m{{ .Name }} \x1b[0m" + `
|
||||
Events: {{ list .Events }}
|
||||
{{- if .Images }}
|
||||
Images: {{ list .Images }}
|
||||
{{- else }}
|
||||
Images: <any>
|
||||
{{- end }}
|
||||
`
|
||||
|
||||
var secretFuncMap = template.FuncMap{
|
||||
"list": func(s []string) string {
|
||||
return strings.Join(s, ", ")
|
||||
},
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var secretDeleteCmd = cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a secret",
|
||||
Action: secretDelete,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretDelete(c *cli.Context) error {
|
||||
var (
|
||||
secret = c.String("name")
|
||||
reponame = c.String("repository")
|
||||
)
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.SecretDelete(owner, name, secret)
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package secret
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var secretUpdateCmd = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update a secret",
|
||||
Action: secretUpdate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repository",
|
||||
Usage: "repository name (e.g. octocat/hello-world)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "secret name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "value",
|
||||
Usage: "secret value",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "event",
|
||||
Usage: "secret limited to these events",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "image",
|
||||
Usage: "secret limited to these images",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func secretUpdate(c *cli.Context) error {
|
||||
reponame := c.String("repository")
|
||||
if reponame == "" {
|
||||
reponame = c.Args().First()
|
||||
}
|
||||
owner, name, err := internal.ParseRepo(reponame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
secret := &drone.Secret{
|
||||
Name: c.String("name"),
|
||||
Value: c.String("value"),
|
||||
Images: c.StringSlice("image"),
|
||||
Events: c.StringSlice("events"),
|
||||
}
|
||||
if strings.HasPrefix(secret.Value, "@") {
|
||||
path := strings.TrimPrefix(secret.Value, "@")
|
||||
out, ferr := ioutil.ReadFile(path)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
secret.Value = string(out)
|
||||
}
|
||||
_, err = client.SecretUpdate(owner, name, secret)
|
||||
return err
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var signCmd = cli.Command{
|
||||
Name: "sign",
|
||||
Usage: "sign the yaml file",
|
||||
Action: sign,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "in",
|
||||
Usage: "input file",
|
||||
Value: ".drone.yml",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "out",
|
||||
Usage: "output file signature",
|
||||
Value: ".drone.yml.sig",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func sign(c *cli.Context) error {
|
||||
repo := c.Args().First()
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
in, err := readInput(c.String("in"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := client.Sign(owner, name, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(c.String("out"), sig, 0664)
|
||||
}
|
142
drone/user.go
142
drone/user.go
|
@ -1,142 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var userCmd = cli.Command{
|
||||
Name: "user",
|
||||
Usage: "manage users",
|
||||
Subcommands: []cli.Command{
|
||||
|
||||
// list command
|
||||
cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list all users",
|
||||
Action: userList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplUserList,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// info command
|
||||
cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show user details",
|
||||
Action: userInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplUserInfo,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// add command
|
||||
cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a user",
|
||||
Action: userAdd,
|
||||
},
|
||||
|
||||
// remove command
|
||||
cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a user",
|
||||
Action: userRemove,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// command to de-register a user from drone server.
|
||||
func userRemove(c *cli.Context) error {
|
||||
login := c.Args().First()
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.UserDel(login); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully removed user %s\n", login)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to register a user with drone server.
|
||||
func userAdd(c *cli.Context) error {
|
||||
login := c.Args().First()
|
||||
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := client.UserPost(&drone.User{Login: login})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully added user %s\n", user.Login)
|
||||
return nil
|
||||
}
|
||||
|
||||
// command to display information for a registered user.
|
||||
func userInfo(c *cli.Context) error {
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
login := c.Args().First()
|
||||
if len(login) == 0 {
|
||||
return fmt.Errorf("Missing or invalid user login")
|
||||
}
|
||||
user, err := client.User(login)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
|
||||
// command to list registered user.
|
||||
func userList(c *cli.Context) error {
|
||||
client, err := newClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := client.UserList()
|
||||
if err != nil || len(users) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, user := range users {
|
||||
tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for user list.
|
||||
var tmplUserList = `{{ .Login }}`
|
||||
|
||||
// template for user info
|
||||
var tmplUserInfo = `User: {{ .Login }}
|
||||
Email: {{ .Email }}`
|
|
@ -0,0 +1,15 @@
|
|||
package user
|
||||
|
||||
import "github.com/urfave/cli"
|
||||
|
||||
// Command exports the user command set.
|
||||
var Command = cli.Command{
|
||||
Name: "user",
|
||||
Usage: "manage users",
|
||||
Subcommands: []cli.Command{
|
||||
userListCmd,
|
||||
userInfoCmd,
|
||||
userAddCmd,
|
||||
userRemoveCmd,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var userAddCmd = cli.Command{
|
||||
Name: "add",
|
||||
Usage: "adds a user",
|
||||
Action: userAdd,
|
||||
}
|
||||
|
||||
func userAdd(c *cli.Context) error {
|
||||
login := c.Args().First()
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := client.UserPost(&drone.User{Login: login})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully added user %s\n", user.Login)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var userInfoCmd = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "show user details",
|
||||
Action: userInfo,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplUserInfo,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func userInfo(c *cli.Context) error {
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
login := c.Args().First()
|
||||
if len(login) == 0 {
|
||||
return fmt.Errorf("Missing or invalid user login")
|
||||
}
|
||||
|
||||
user, err := client.User(login)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
|
||||
// template for user information
|
||||
var tmplUserInfo = `User: {{ .Login }}
|
||||
Email: {{ .Email }}`
|
|
@ -0,0 +1,47 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var userListCmd = cli.Command{
|
||||
Name: "ls",
|
||||
Usage: "list all users",
|
||||
Action: userList,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "format",
|
||||
Usage: "format output",
|
||||
Value: tmplUserList,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func userList(c *cli.Context) error {
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := client.UserList()
|
||||
if err != nil || len(users) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpl, err := template.New("_").Parse(c.String("format") + "\n")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, user := range users {
|
||||
tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// template for user list items
|
||||
var tmplUserList = `{{ .Login }}`
|
|
@ -0,0 +1,30 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/drone/drone-cli/drone/internal"
|
||||
)
|
||||
|
||||
var userRemoveCmd = cli.Command{
|
||||
Name: "rm",
|
||||
Usage: "remove a user",
|
||||
Action: userRemove,
|
||||
}
|
||||
|
||||
func userRemove(c *cli.Context) error {
|
||||
login := c.Args().First()
|
||||
|
||||
client, err := internal.NewClient(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.UserDel(login); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Successfully removed user %s\n", login)
|
||||
return nil
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/jackspirou/syscerts"
|
||||
)
|
||||
|
||||
func newClient(c *cli.Context) (drone.Client, error) {
|
||||
var token = c.GlobalString("token")
|
||||
var server = c.GlobalString("server")
|
||||
|
||||
// if no server url is provided we can default
|
||||
// to the hosted Drone service.
|
||||
if len(server) == 0 {
|
||||
return nil, fmt.Errorf("Error: you must provide the Drone server address.")
|
||||
}
|
||||
if len(token) == 0 {
|
||||
return nil, fmt.Errorf("Error: you must provide your Drone access token.")
|
||||
}
|
||||
|
||||
// attempt to find system CA certs
|
||||
certs := syscerts.SystemRootsPool()
|
||||
tlsConfig := &tls.Config{RootCAs: certs}
|
||||
|
||||
// create the drone client with TLS options
|
||||
return drone.NewClientTokenTLS(server, token, tlsConfig), nil
|
||||
}
|
||||
|
||||
func parseRepo(str string) (user, repo string, err error) {
|
||||
var parts = strings.Split(str, "/")
|
||||
if len(parts) != 2 {
|
||||
err = fmt.Errorf("Error: Invalid or missing repository. eg octocat/hello-world.")
|
||||
return
|
||||
}
|
||||
user = parts[0]
|
||||
repo = parts[1]
|
||||
return
|
||||
}
|
||||
|
||||
func readInput(in string) ([]byte, error) {
|
||||
if in == "-" {
|
||||
return ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
return ioutil.ReadFile(in)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
|
||||
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,268 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||
|
||||
const (
|
||||
BackupData = uint32(iota + 1)
|
||||
BackupEaData
|
||||
BackupSecurity
|
||||
BackupAlternateData
|
||||
BackupLink
|
||||
BackupPropertyData
|
||||
BackupObjectId
|
||||
BackupReparseData
|
||||
BackupSparseBlock
|
||||
BackupTxfsData
|
||||
)
|
||||
|
||||
const (
|
||||
StreamSparseAttributes = uint32(8)
|
||||
)
|
||||
|
||||
const (
|
||||
WRITE_DAC = 0x40000
|
||||
WRITE_OWNER = 0x80000
|
||||
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||
)
|
||||
|
||||
// BackupHeader represents a backup stream of a file.
|
||||
type BackupHeader struct {
|
||||
Id uint32 // The backup stream ID
|
||||
Attributes uint32 // Stream attributes
|
||||
Size int64 // The size of the stream in bytes
|
||||
Name string // The name of the stream (for BackupAlternateData only).
|
||||
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||
}
|
||||
|
||||
type win32StreamId struct {
|
||||
StreamId uint32
|
||||
Attributes uint32
|
||||
Size uint64
|
||||
NameSize uint32
|
||||
}
|
||||
|
||||
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||
// of BackupHeader values.
|
||||
type BackupStreamReader struct {
|
||||
r io.Reader
|
||||
bytesLeft int64
|
||||
}
|
||||
|
||||
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||
return &BackupStreamReader{r, 0}
|
||||
}
|
||||
|
||||
// Next returns the next backup stream and prepares for calls to Write(). It skips the remainder of the current stream if
|
||||
// it was not completely read.
|
||||
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||
if r.bytesLeft > 0 {
|
||||
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var wsi win32StreamId
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr := &BackupHeader{
|
||||
Id: wsi.StreamId,
|
||||
Attributes: wsi.Attributes,
|
||||
Size: int64(wsi.Size),
|
||||
}
|
||||
if wsi.NameSize != 0 {
|
||||
name := make([]uint16, int(wsi.NameSize/2))
|
||||
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr.Name = syscall.UTF16ToString(name)
|
||||
}
|
||||
if wsi.StreamId == BackupSparseBlock {
|
||||
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hdr.Size -= 8
|
||||
}
|
||||
r.bytesLeft = hdr.Size
|
||||
return hdr, nil
|
||||
}
|
||||
|
||||
// Read reads from the current backup stream.
|
||||
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||
if r.bytesLeft == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if int64(len(b)) > r.bytesLeft {
|
||||
b = b[:r.bytesLeft]
|
||||
}
|
||||
n, err := r.r.Read(b)
|
||||
r.bytesLeft -= int64(n)
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
} else if r.bytesLeft == 0 && err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||
type BackupStreamWriter struct {
|
||||
w io.Writer
|
||||
bytesLeft int64
|
||||
}
|
||||
|
||||
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||
return &BackupStreamWriter{w, 0}
|
||||
}
|
||||
|
||||
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||
if w.bytesLeft != 0 {
|
||||
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||
}
|
||||
name := utf16.Encode([]rune(hdr.Name))
|
||||
wsi := win32StreamId{
|
||||
StreamId: hdr.Id,
|
||||
Attributes: hdr.Attributes,
|
||||
Size: uint64(hdr.Size),
|
||||
NameSize: uint32(len(name) * 2),
|
||||
}
|
||||
if hdr.Id == BackupSparseBlock {
|
||||
// Include space for the int64 block offset
|
||||
wsi.Size += 8
|
||||
}
|
||||
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(name) != 0 {
|
||||
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if hdr.Id == BackupSparseBlock {
|
||||
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.bytesLeft = hdr.Size
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes to the current backup stream.
|
||||
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||
if w.bytesLeft < int64(len(b)) {
|
||||
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||
}
|
||||
n, err := w.w.Write(b)
|
||||
w.bytesLeft -= int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||
type BackupFileReader struct {
|
||||
f *os.File
|
||||
includeSecurity bool
|
||||
ctx uintptr
|
||||
}
|
||||
|
||||
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||
// Read will attempt to read the security descriptor of the file.
|
||||
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||
r := &BackupFileReader{f, includeSecurity, 0}
|
||||
runtime.SetFinalizer(r, func(r *BackupFileReader) { r.Close() })
|
||||
return r
|
||||
}
|
||||
|
||||
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||
var bytesRead uint32
|
||||
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||
}
|
||||
if bytesRead == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(bytesRead), nil
|
||||
}
|
||||
|
||||
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||
// the underlying file.
|
||||
func (r *BackupFileReader) Close() error {
|
||||
if r.ctx != 0 {
|
||||
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||
r.ctx = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||
type BackupFileWriter struct {
|
||||
f *os.File
|
||||
includeSecurity bool
|
||||
ctx uintptr
|
||||
}
|
||||
|
||||
// NewBackupFileWrtier returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||
// Write() will attempt to restore the security descriptor from the stream.
|
||||
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||
runtime.SetFinalizer(w, func(w *BackupFileWriter) { w.Close() })
|
||||
return w
|
||||
}
|
||||
|
||||
// Write restores a portion of the file using the provided backup stream.
|
||||
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||
var bytesWritten uint32
|
||||
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||
}
|
||||
if int(bytesWritten) != len(b) {
|
||||
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||
// close the underlying file.
|
||||
func (w *BackupFileWriter) Close() error {
|
||||
if w.ctx != 0 {
|
||||
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||
w.ctx = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||
// or restore privileges have been acquired.
|
||||
//
|
||||
// If the file opened was a directory, it cannot be used with Readdir().
|
||||
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||
winPath, err := syscall.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(h), path), nil
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||
//sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod
|
||||
|
||||
const (
|
||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFileClosed = errors.New("file has already been closed")
|
||||
ErrTimeout = &timeoutError{}
|
||||
)
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *timeoutError) Timeout() bool { return true }
|
||||
func (e *timeoutError) Temporary() bool { return true }
|
||||
|
||||
var ioInitOnce sync.Once
|
||||
var ioCompletionPort syscall.Handle
|
||||
|
||||
// ioResult contains the result of an asynchronous IO operation
|
||||
type ioResult struct {
|
||||
bytes uint32
|
||||
err error
|
||||
}
|
||||
|
||||
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||
type ioOperation struct {
|
||||
o syscall.Overlapped
|
||||
ch chan ioResult
|
||||
}
|
||||
|
||||
func initIo() {
|
||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ioCompletionPort = h
|
||||
go ioCompletionProcessor(h)
|
||||
}
|
||||
|
||||
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||
type win32File struct {
|
||||
handle syscall.Handle
|
||||
wg sync.WaitGroup
|
||||
closing bool
|
||||
readDeadline time.Time
|
||||
writeDeadline time.Time
|
||||
}
|
||||
|
||||
// makeWin32File makes a new win32File from an existing file handle
|
||||
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||
f := &win32File{handle: h}
|
||||
ioInitOnce.Do(initIo)
|
||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtime.SetFinalizer(f, (*win32File).closeHandle)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||
return makeWin32File(h)
|
||||
}
|
||||
|
||||
// closeHandle closes the resources associated with a Win32 handle
|
||||
func (f *win32File) closeHandle() {
|
||||
if !f.closing {
|
||||
// cancel all IO and wait for it to complete
|
||||
f.closing = true
|
||||
cancelIoEx(f.handle, nil)
|
||||
f.wg.Wait()
|
||||
// at this point, no new IO can start
|
||||
syscall.Close(f.handle)
|
||||
f.handle = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes a win32File.
|
||||
func (f *win32File) Close() error {
|
||||
f.closeHandle()
|
||||
runtime.SetFinalizer(f, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareIo prepares for a new IO operation
|
||||
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||
f.wg.Add(1)
|
||||
if f.closing {
|
||||
return nil, ErrFileClosed
|
||||
}
|
||||
c := &ioOperation{}
|
||||
c.ch = make(chan ioResult)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ioCompletionProcessor processes completed async IOs forever
|
||||
func ioCompletionProcessor(h syscall.Handle) {
|
||||
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
|
||||
timeBeginPeriod(1)
|
||||
for {
|
||||
var bytes uint32
|
||||
var key uintptr
|
||||
var op *ioOperation
|
||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||
if op == nil {
|
||||
panic(err)
|
||||
}
|
||||
op.ch <- ioResult{bytes, err}
|
||||
}
|
||||
}
|
||||
|
||||
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||
// the operation has actually completed.
|
||||
func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) {
|
||||
if err != syscall.ERROR_IO_PENDING {
|
||||
f.wg.Done()
|
||||
return int(bytes), err
|
||||
} else {
|
||||
var r ioResult
|
||||
wait := true
|
||||
timedout := false
|
||||
if f.closing {
|
||||
cancelIoEx(f.handle, &c.o)
|
||||
} else if !deadline.IsZero() {
|
||||
now := time.Now()
|
||||
if !deadline.After(now) {
|
||||
timedout = true
|
||||
} else {
|
||||
timeout := time.After(deadline.Sub(now))
|
||||
select {
|
||||
case r = <-c.ch:
|
||||
wait = false
|
||||
case <-timeout:
|
||||
timedout = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if timedout {
|
||||
cancelIoEx(f.handle, &c.o)
|
||||
}
|
||||
if wait {
|
||||
r = <-c.ch
|
||||
}
|
||||
err = r.err
|
||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||
if f.closing {
|
||||
err = ErrFileClosed
|
||||
} else if timedout {
|
||||
err = ErrTimeout
|
||||
}
|
||||
}
|
||||
f.wg.Done()
|
||||
return int(r.bytes), err
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads from a file handle.
|
||||
func (f *win32File) Read(b []byte) (int, error) {
|
||||
c, err := f.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var bytes uint32
|
||||
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||
n, err := f.asyncIo(c, f.readDeadline, bytes, err)
|
||||
|
||||
// Handle EOF conditions.
|
||||
if err == nil && n == 0 && len(b) != 0 {
|
||||
return 0, io.EOF
|
||||
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||
return 0, io.EOF
|
||||
} else {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes to a file handle.
|
||||
func (f *win32File) Write(b []byte) (int, error) {
|
||||
c, err := f.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var bytes uint32
|
||||
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||
return f.asyncIo(c, f.writeDeadline, bytes, err)
|
||||
}
|
||||
|
||||
func (f *win32File) SetReadDeadline(t time.Time) error {
|
||||
f.readDeadline = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *win32File) SetWriteDeadline(t time.Time) error {
|
||||
f.writeDeadline = t
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||
|
||||
const (
|
||||
fileBasicInfo = 0
|
||||
fileIDInfo = 0x12
|
||||
)
|
||||
|
||||
// FileBasicInfo contains file access time and file attributes information.
|
||||
type FileBasicInfo struct {
|
||||
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
||||
FileAttributes uintptr // includes padding
|
||||
}
|
||||
|
||||
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||
bi := &FileBasicInfo{}
|
||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// SetFileBasicInfo sets times and attributes for a file.
|
||||
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||
// unique on a system.
|
||||
type FileIDInfo struct {
|
||||
VolumeSerialNumber uint64
|
||||
FileID [16]byte
|
||||
}
|
||||
|
||||
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||
fileID := &FileIDInfo{}
|
||||
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||
}
|
||||
return fileID, nil
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||
//sys createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||
//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW
|
||||
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||
|
||||
type securityAttributes struct {
|
||||
Length uint32
|
||||
SecurityDescriptor *byte
|
||||
InheritHandle uint32
|
||||
}
|
||||
|
||||
const (
|
||||
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||
|
||||
cPIPE_ACCESS_DUPLEX = 0x3
|
||||
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000
|
||||
cSECURITY_SQOS_PRESENT = 0x100000
|
||||
cSECURITY_ANONYMOUS = 0
|
||||
|
||||
cPIPE_REJECT_REMOTE_CLIENTS = 0x8
|
||||
|
||||
cPIPE_UNLIMITED_INSTANCES = 255
|
||||
|
||||
cNMPWAIT_USE_DEFAULT_WAIT = 0
|
||||
cNMPWAIT_NOWAIT = 1
|
||||
|
||||
cPIPE_TYPE_MESSAGE = 4
|
||||
|
||||
cPIPE_READMODE_MESSAGE = 2
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
||||
|
||||
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||
)
|
||||
|
||||
type win32Pipe struct {
|
||||
*win32File
|
||||
path string
|
||||
}
|
||||
|
||||
type win32MessageBytePipe struct {
|
||||
win32Pipe
|
||||
writeClosed bool
|
||||
readEOF bool
|
||||
}
|
||||
|
||||
type pipeAddress string
|
||||
|
||||
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||
return pipeAddress(f.path)
|
||||
}
|
||||
|
||||
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||
return pipeAddress(f.path)
|
||||
}
|
||||
|
||||
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||
f.SetReadDeadline(t)
|
||||
f.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||
if f.writeClosed {
|
||||
return errPipeWriteClosed
|
||||
}
|
||||
_, err := f.win32File.Write(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.writeClosed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||
// they are used to implement CloseWrite().
|
||||
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
||||
if f.writeClosed {
|
||||
return 0, errPipeWriteClosed
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return f.win32File.Write(b)
|
||||
}
|
||||
|
||||
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||
if f.readEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err := f.win32File.Read(b)
|
||||
if err == io.EOF {
|
||||
// If this was the result of a zero-byte read, then
|
||||
// it is possible that the read was due to a zero-size
|
||||
// message. Since we are simulating CloseWrite with a
|
||||
// zero-byte message, ensure that all future Read() calls
|
||||
// also return EOF.
|
||||
f.readEOF = true
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s pipeAddress) Network() string {
|
||||
return "pipe"
|
||||
}
|
||||
|
||||
func (s pipeAddress) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||
// takes longer than the specified duration. If timeout is nil, then the timeout
|
||||
// is the default timeout established by the pipe server.
|
||||
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||
var absTimeout time.Time
|
||||
if timeout != nil {
|
||||
absTimeout = time.Now().Add(*timeout)
|
||||
}
|
||||
var err error
|
||||
var h syscall.Handle
|
||||
for {
|
||||
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||
if err != cERROR_PIPE_BUSY {
|
||||
break
|
||||
}
|
||||
now := time.Now()
|
||||
var ms uint32
|
||||
if absTimeout.IsZero() {
|
||||
ms = cNMPWAIT_USE_DEFAULT_WAIT
|
||||
} else if now.After(absTimeout) {
|
||||
ms = cNMPWAIT_NOWAIT
|
||||
} else {
|
||||
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000)
|
||||
}
|
||||
err = waitNamedPipe(path, ms)
|
||||
if err != nil {
|
||||
if err == cERROR_SEM_TIMEOUT {
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
|
||||
var flags uint32
|
||||
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var state uint32
|
||||
err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if state&cPIPE_READMODE_MESSAGE != 0 {
|
||||
return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")}
|
||||
}
|
||||
|
||||
f, err := makeWin32File(h)
|
||||
if err != nil {
|
||||
syscall.Close(h)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the pipe is in message mode, return a message byte pipe, which
|
||||
// supports CloseWrite().
|
||||
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
||||
return &win32MessageBytePipe{
|
||||
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||
}, nil
|
||||
}
|
||||
return &win32Pipe{win32File: f, path: path}, nil
|
||||
}
|
||||
|
||||
type acceptResponse struct {
|
||||
f *win32File
|
||||
err error
|
||||
}
|
||||
|
||||
type win32PipeListener struct {
|
||||
firstHandle syscall.Handle
|
||||
path string
|
||||
securityDescriptor []byte
|
||||
config PipeConfig
|
||||
acceptCh chan (chan acceptResponse)
|
||||
closeCh chan int
|
||||
doneCh chan int
|
||||
}
|
||||
|
||||
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
||||
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED
|
||||
if first {
|
||||
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE
|
||||
}
|
||||
|
||||
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS
|
||||
if c.MessageMode {
|
||||
mode |= cPIPE_TYPE_MESSAGE
|
||||
}
|
||||
|
||||
var sa securityAttributes
|
||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
if securityDescriptor != nil {
|
||||
sa.SecurityDescriptor = &securityDescriptor[0]
|
||||
}
|
||||
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, &sa)
|
||||
if err != nil {
|
||||
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := makeWin32File(h)
|
||||
if err != nil {
|
||||
syscall.Close(h)
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) listenerRoutine() {
|
||||
closed := false
|
||||
for !closed {
|
||||
select {
|
||||
case <-l.closeCh:
|
||||
closed = true
|
||||
case responseCh := <-l.acceptCh:
|
||||
p, err := l.makeServerPipe()
|
||||
if err == nil {
|
||||
// Wait for the client to connect.
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
ch <- connectPipe(p)
|
||||
}()
|
||||
select {
|
||||
case err = <-ch:
|
||||
if err != nil {
|
||||
p.Close()
|
||||
p = nil
|
||||
}
|
||||
case <-l.closeCh:
|
||||
// Abort the connect request by closing the handle.
|
||||
p.Close()
|
||||
p = nil
|
||||
err = <-ch
|
||||
if err == nil || err == ErrFileClosed {
|
||||
err = ErrPipeListenerClosed
|
||||
}
|
||||
closed = true
|
||||
}
|
||||
}
|
||||
responseCh <- acceptResponse{p, err}
|
||||
}
|
||||
}
|
||||
syscall.Close(l.firstHandle)
|
||||
l.firstHandle = 0
|
||||
// Notify Close() and Accept() callers that the handle has been closed.
|
||||
close(l.doneCh)
|
||||
}
|
||||
|
||||
// PipeConfig contain configuration for the pipe listener.
|
||||
type PipeConfig struct {
|
||||
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||
SecurityDescriptor string
|
||||
|
||||
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||
// case the pipe is read in byte mode by default. The only practical difference in
|
||||
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||
// when the pipe is in message mode.
|
||||
MessageMode bool
|
||||
|
||||
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||
InputBufferSize int32
|
||||
|
||||
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||
OutputBufferSize int32
|
||||
}
|
||||
|
||||
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||
// The pipe must not already exist.
|
||||
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||
var (
|
||||
sd []byte
|
||||
err error
|
||||
)
|
||||
if c == nil {
|
||||
c = &PipeConfig{}
|
||||
}
|
||||
if c.SecurityDescriptor != "" {
|
||||
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
h, err := makeServerPipeHandle(path, sd, c, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Immediately open and then close a client handle so that the named pipe is
|
||||
// created but not currently accepting connections.
|
||||
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||
if err != nil {
|
||||
syscall.Close(h)
|
||||
return nil, err
|
||||
}
|
||||
syscall.Close(h2)
|
||||
l := &win32PipeListener{
|
||||
firstHandle: h,
|
||||
path: path,
|
||||
securityDescriptor: sd,
|
||||
config: *c,
|
||||
acceptCh: make(chan (chan acceptResponse)),
|
||||
closeCh: make(chan int),
|
||||
doneCh: make(chan int),
|
||||
}
|
||||
go l.listenerRoutine()
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func connectPipe(p *win32File) error {
|
||||
c, err := p.prepareIo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = connectNamedPipe(p.handle, &c.o)
|
||||
_, err = p.asyncIo(c, time.Time{}, 0, err)
|
||||
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||
ch := make(chan acceptResponse)
|
||||
select {
|
||||
case l.acceptCh <- ch:
|
||||
response := <-ch
|
||||
err := response.err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l.config.MessageMode {
|
||||
return &win32MessageBytePipe{
|
||||
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
||||
}, nil
|
||||
}
|
||||
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
||||
case <-l.doneCh:
|
||||
return nil, ErrPipeListenerClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Close() error {
|
||||
select {
|
||||
case l.closeCh <- 1:
|
||||
<-l.doneCh
|
||||
case <-l.doneCh:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *win32PipeListener) Addr() net.Addr {
|
||||
return pipeAddress(l.path)
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||
|
||||
const (
|
||||
SE_PRIVILEGE_ENABLED = 2
|
||||
|
||||
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||
|
||||
SeBackupPrivilege = "SeBackupPrivilege"
|
||||
SeRestorePrivilege = "SeRestorePrivilege"
|
||||
)
|
||||
|
||||
const (
|
||||
securityAnonymous = iota
|
||||
securityIdentification
|
||||
securityImpersonation
|
||||
securityDelegation
|
||||
)
|
||||
|
||||
var (
|
||||
privNames = make(map[string]uint64)
|
||||
privNameMutex sync.Mutex
|
||||
)
|
||||
|
||||
// PrivilegeError represents an error enabling privileges.
|
||||
type PrivilegeError struct {
|
||||
privileges []uint64
|
||||
}
|
||||
|
||||
func (e *PrivilegeError) Error() string {
|
||||
s := ""
|
||||
if len(e.privileges) > 1 {
|
||||
s = "Could not enable privileges "
|
||||
} else {
|
||||
s = "Could not enable privilege "
|
||||
}
|
||||
for i, p := range e.privileges {
|
||||
if i != 0 {
|
||||
s += ", "
|
||||
}
|
||||
s += `"`
|
||||
s += getPrivilegeName(p)
|
||||
s += `"`
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RunWithPrivilege enables a single privilege for a function call.
|
||||
func RunWithPrivilege(name string, fn func() error) error {
|
||||
return RunWithPrivileges([]string{name}, fn)
|
||||
}
|
||||
|
||||
// RunWithPrivileges enables privileges for a function call.
|
||||
func RunWithPrivileges(names []string, fn func() error) error {
|
||||
privileges, err := mapPrivileges(names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
token, err := newThreadToken()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer releaseThreadToken(token)
|
||||
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fn()
|
||||
}
|
||||
|
||||
func mapPrivileges(names []string) ([]uint64, error) {
|
||||
var privileges []uint64
|
||||
privNameMutex.Lock()
|
||||
defer privNameMutex.Unlock()
|
||||
for _, name := range names {
|
||||
p, ok := privNames[name]
|
||||
if !ok {
|
||||
err := lookupPrivilegeValue("", name, &p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privNames[name] = p
|
||||
}
|
||||
privileges = append(privileges, p)
|
||||
}
|
||||
return privileges, nil
|
||||
}
|
||||
|
||||
// EnableProcessPrivileges enables privileges globally for the process.
|
||||
func EnableProcessPrivileges(names []string) error {
|
||||
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
||||
}
|
||||
|
||||
// DisableProcessPrivileges disables privileges globally for the process.
|
||||
func DisableProcessPrivileges(names []string) error {
|
||||
return enableDisableProcessPrivilege(names, 0)
|
||||
}
|
||||
|
||||
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||
privileges, err := mapPrivileges(names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, _ := windows.GetCurrentProcess()
|
||||
var token windows.Token
|
||||
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer token.Close()
|
||||
return adjustPrivileges(token, privileges, action)
|
||||
}
|
||||
|
||||
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||
var b bytes.Buffer
|
||||
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||
for _, p := range privileges {
|
||||
binary.Write(&b, binary.LittleEndian, p)
|
||||
binary.Write(&b, binary.LittleEndian, action)
|
||||
}
|
||||
prevState := make([]byte, b.Len())
|
||||
reqSize := uint32(0)
|
||||
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
||||
if !success {
|
||||
return err
|
||||
}
|
||||
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||
return &PrivilegeError{privileges}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPrivilegeName(luid uint64) string {
|
||||
var nameBuffer [256]uint16
|
||||
bufSize := uint32(len(nameBuffer))
|
||||
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<unknown privilege %d>", luid)
|
||||
}
|
||||
|
||||
var displayNameBuffer [256]uint16
|
||||
displayBufSize := uint32(len(displayNameBuffer))
|
||||
var langID uint32
|
||||
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
||||
}
|
||||
|
||||
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
||||
}
|
||||
|
||||
func newThreadToken() (windows.Token, error) {
|
||||
err := impersonateSelf(securityImpersonation)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var token windows.Token
|
||||
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
||||
if err != nil {
|
||||
rerr := revertToSelf()
|
||||
if rerr != nil {
|
||||
panic(rerr)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func releaseThreadToken(h windows.Token) {
|
||||
err := revertToSelf()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
h.Close()
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package winio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
reparseTagMountPoint = 0xA0000003
|
||||
reparseTagSymlink = 0xA000000C
|
||||
)
|
||||
|
||||
type reparseDataBuffer struct {
|
||||
ReparseTag uint32
|
||||
ReparseDataLength uint16
|
||||
Reserved uint16
|
||||
SubstituteNameOffset uint16
|
||||
SubstituteNameLength uint16
|
||||
PrintNameOffset uint16
|
||||
PrintNameLength uint16
|
||||
}
|
||||
|
||||
// ReparsePoint describes a Win32 symlink or mount point.
|
||||
type ReparsePoint struct {
|
||||
Target string
|
||||
IsMountPoint bool
|
||||
}
|
||||
|
||||
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||
// mount point reparse point.
|
||||
type UnsupportedReparsePointError struct {
|
||||
Tag uint32
|
||||
}
|
||||
|
||||
func (e *UnsupportedReparsePointError) Error() string {
|
||||
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
||||
}
|
||||
|
||||
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||
// or a mount point.
|
||||
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
||||
tag := binary.LittleEndian.Uint32(b[0:4])
|
||||
return DecodeReparsePointData(tag, b[8:])
|
||||
}
|
||||
|
||||
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
||||
isMountPoint := false
|
||||
switch tag {
|
||||
case reparseTagMountPoint:
|
||||
isMountPoint = true
|
||||
case reparseTagSymlink:
|
||||
default:
|
||||
return nil, &UnsupportedReparsePointError{tag}
|
||||
}
|
||||
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
||||
if !isMountPoint {
|
||||
nameOffset += 4
|
||||
}
|
||||
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
||||
name := make([]uint16, nameLength/2)
|
||||
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
||||
}
|
||||
|
||||
func isDriveLetter(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
}
|
||||
|
||||
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||
// mount point.
|
||||
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||
// Generate an NT path and determine if this is a relative path.
|
||||
var ntTarget string
|
||||
relative := false
|
||||
if strings.HasPrefix(rp.Target, `\\?\`) {
|
||||
ntTarget = `\??\` + rp.Target[4:]
|
||||
} else if strings.HasPrefix(rp.Target, `\\`) {
|
||||
ntTarget = `\??\UNC\` + rp.Target[2:]
|
||||
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
||||
ntTarget = `\??\` + rp.Target
|
||||
} else {
|
||||
ntTarget = rp.Target
|
||||
relative = true
|
||||
}
|
||||
|
||||
// The paths must be NUL-terminated even though they are counted strings.
|
||||
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
||||
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
||||
|
||||
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
||||
size += len(ntTarget16)*2 + len(target16)*2
|
||||
|
||||
tag := uint32(reparseTagMountPoint)
|
||||
if !rp.IsMountPoint {
|
||||
tag = reparseTagSymlink
|
||||
size += 4 // Add room for symlink flags
|
||||
}
|
||||
|
||||
data := reparseDataBuffer{
|
||||
ReparseTag: tag,
|
||||
ReparseDataLength: uint16(size),
|
||||
SubstituteNameOffset: 0,
|
||||
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
||||
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
||||
PrintNameLength: uint16((len(target16) - 1) * 2),
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
binary.Write(&b, binary.LittleEndian, &data)
|
||||
if !rp.IsMountPoint {
|
||||
flags := uint32(0)
|
||||
if relative {
|
||||
flags |= 1
|
||||
}
|
||||
binary.Write(&b, binary.LittleEndian, flags)
|
||||
}
|
||||
|
||||
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||
binary.Write(&b, binary.LittleEndian, target16)
|
||||
return b.Bytes()
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// +build windows
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||
//sys localFree(mem uintptr) = LocalFree
|
||||
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||
|
||||
const (
|
||||
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
||||
)
|
||||
|
||||
type AccountLookupError struct {
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *AccountLookupError) Error() string {
|
||||
if e.Name == "" {
|
||||
return "lookup account: empty account name specified"
|
||||
}
|
||||
var s string
|
||||
switch e.Err {
|
||||
case cERROR_NONE_MAPPED:
|
||||
s = "not found"
|
||||
default:
|
||||
s = e.Err.Error()
|
||||
}
|
||||
return "lookup account " + e.Name + ": " + s
|
||||
}
|
||||
|
||||
type SddlConversionError struct {
|
||||
Sddl string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SddlConversionError) Error() string {
|
||||
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// LookupSidByName looks up the SID of an account by name
|
||||
func LookupSidByName(name string) (sid string, err error) {
|
||||
if name == "" {
|
||||
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
||||
}
|
||||
|
||||
var sidSize, sidNameUse, refDomainSize uint32
|
||||
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
sidBuffer := make([]byte, sidSize)
|
||||
refDomainBuffer := make([]uint16, refDomainSize)
|
||||
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||
if err != nil {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
var strBuffer *uint16
|
||||
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
||||
if err != nil {
|
||||
return "", &AccountLookupError{name, err}
|
||||
}
|
||||
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
||||
return sid, nil
|
||||
}
|
||||
|
||||
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||
var sdBuffer uintptr
|
||||
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
||||
if err != nil {
|
||||
return nil, &SddlConversionError{sddl, err}
|
||||
}
|
||||
defer localFree(sdBuffer)
|
||||
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
||||
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||
var sddl *uint16
|
||||
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||
// Don't use it.
|
||||
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
||||
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package winio
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
|
@ -0,0 +1,496 @@
|
|||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package winio
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
modwinmm = windows.NewLazySystemDLL("winmm.dll")
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
|
||||
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
|
||||
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||
)
|
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
if newport == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func timeBeginPeriod(period uint32) (n int32) {
|
||||
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||
n = int32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||
}
|
||||
|
||||
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *securityAttributes) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createFile(name string, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||
}
|
||||
|
||||
func _createFile(name *uint16, access uint32, mode uint32, sa *securityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||
handle = syscall.Handle(r0)
|
||||
if handle == syscall.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func waitNamedPipe(name string, timeout uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _waitNamedPipe(_p0, timeout)
|
||||
}
|
||||
|
||||
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||
}
|
||||
|
||||
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(str)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||
}
|
||||
|
||||
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localFree(mem uintptr) {
|
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||
len = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||
var _p0 uint32
|
||||
if releaseAll {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||
success = r0 != 0
|
||||
if true {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func impersonateSelf(level uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func revertToSelf() (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||
var _p0 uint32
|
||||
if openAsSelf {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getCurrentThread() (h syscall.Handle) {
|
||||
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||
h = syscall.Handle(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *uint16
|
||||
_p1, err = syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeValue(_p0, _p1, luid)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
var _p0 *uint16
|
||||
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||
}
|
||||
|
||||
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||
var _p0 *byte
|
||||
if len(b) > 0 {
|
||||
_p0 = &b[0]
|
||||
}
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
} else {
|
||||
_p1 = 0
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
} else {
|
||||
_p2 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||
var _p0 *byte
|
||||
if len(b) > 0 {
|
||||
_p0 = &b[0]
|
||||
}
|
||||
var _p1 uint32
|
||||
if abort {
|
||||
_p1 = 1
|
||||
} else {
|
||||
_p1 = 0
|
||||
}
|
||||
var _p2 uint32
|
||||
if processSecurity {
|
||||
_p2 = 1
|
||||
} else {
|
||||
_p2 = 0
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
# 0.10.0
|
||||
|
||||
* feature: Add a test hook (#180)
|
||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||
* performance: avoid re-allocations on `WithFields` (#335)
|
||||
|
||||
# 0.9.0
|
||||
|
||||
* logrus/text_formatter: don't emit empty msg
|
||||
* logrus/hooks/airbrake: move out of main repository
|
||||
* logrus/hooks/sentry: move out of main repository
|
||||
* logrus/hooks/papertrail: move out of main repository
|
||||
* logrus/hooks/bugsnag: move out of main repository
|
||||
* logrus/core: run tests with `-race`
|
||||
* logrus/core: detect TTY based on `stderr`
|
||||
* logrus/core: support `WithError` on logger
|
||||
* logrus/core: Solaris support
|
||||
|
||||
# 0.8.7
|
||||
|
||||
* logrus/core: fix possible race (#216)
|
||||
* logrus/doc: small typo fixes and doc improvements
|
||||
|
||||
|
||||
# 0.8.6
|
||||
|
||||
* hooks/raven: allow passing an initialized client
|
||||
|
||||
# 0.8.5
|
||||
|
||||
* logrus/core: revert #208
|
||||
|
||||
# 0.8.4
|
||||
|
||||
* formatter/text: fix data race (#218)
|
||||
|
||||
# 0.8.3
|
||||
|
||||
* logrus/core: fix entry log level (#208)
|
||||
* logrus/core: improve performance of text formatter by 40%
|
||||
* logrus/core: expose `LevelHooks` type
|
||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||
* formatter/text: print structs more verbosely
|
||||
|
||||
# 0.8.2
|
||||
|
||||
* logrus: fix more Fatal family functions
|
||||
|
||||
# 0.8.1
|
||||
|
||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||
|
||||
# 0.8.0
|
||||
|
||||
* logrus: defaults to stderr instead of stdout
|
||||
* hooks/sentry: add special field for `*http.Request`
|
||||
* formatter/text: ignore Windows for colors
|
||||
|
||||
# 0.7.3
|
||||
|
||||
* formatter/\*: allow configuration of timestamp layout
|
||||
|
||||
# 0.7.2
|
||||
|
||||
* formatter/text: Add configuration option for time format (#158)
|
|
@ -1,388 +0,0 @@
|
|||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
|
||||
many large deployments. The core API is unlikely to change much but please
|
||||
version control your Logrus to make sure you aren't fetching latest `master` on
|
||||
every build.**
|
||||
|
||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||
plain text):
|
||||
|
||||
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||
|
||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||
|
||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||
|
||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||
|
||||
```text
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||
exit status 1
|
||||
```
|
||||
|
||||
#### Example
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
```
|
||||
|
||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
|
||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||
want:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Log as JSON instead of the default ASCII formatter.
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
|
||||
// Output to stderr instead of stdout, could also be a file.
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
// Only log the warning severity or above.
|
||||
log.SetLevel(log.WarnLevel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
|
||||
// A common pattern is to re-use fields between logging statements by re-using
|
||||
// the logrus.Entry returned from WithFields()
|
||||
contextLogger := log.WithFields(log.Fields{
|
||||
"common": "this is a common field",
|
||||
"other": "I also should be logged always",
|
||||
})
|
||||
|
||||
contextLogger.Info("I'll be logged with common and other field")
|
||||
contextLogger.Info("Me too")
|
||||
}
|
||||
```
|
||||
|
||||
For more advanced usage such as logging to multiple locations from the same
|
||||
application, you can also create an instance of the `logrus` Logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Create a new instance of the logger. You can have any number of instances.
|
||||
var log = logrus.New()
|
||||
|
||||
func main() {
|
||||
// The API for setting attributes is a little different than the package level
|
||||
// exported logger. See Godoc.
|
||||
log.Out = os.Stderr
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
}
|
||||
```
|
||||
|
||||
#### Fields
|
||||
|
||||
Logrus encourages careful, structured logging though logging fields instead of
|
||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||
to send event %s to topic %s with key %d")`, you should log the much more
|
||||
discoverable:
|
||||
|
||||
```go
|
||||
log.WithFields(log.Fields{
|
||||
"event": event,
|
||||
"topic": topic,
|
||||
"key": key,
|
||||
}).Fatal("Failed to send event")
|
||||
```
|
||||
|
||||
We've found this API forces you to think about logging in a way that produces
|
||||
much more useful logging messages. We've been in countless situations where just
|
||||
a single added field to a log statement that was already there would've saved us
|
||||
hours. The `WithFields` call is optional.
|
||||
|
||||
In general, with Logrus using any of the `printf`-family functions should be
|
||||
seen as a hint you should add a field, however, you can still use the
|
||||
`printf`-family functions with Logrus.
|
||||
|
||||
#### Hooks
|
||||
|
||||
You can add hooks for logging levels. For example to send errors to an exception
|
||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||
multiple places simultaneously, e.g. syslog.
|
||||
|
||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||
`init`:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
if err != nil {
|
||||
log.Error("Unable to connect to local syslog daemon")
|
||||
} else {
|
||||
log.AddHook(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||
|
||||
| Hook | Description |
|
||||
| ----- | ----------- |
|
||||
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
||||
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
||||
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
|
||||
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
||||
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
|
||||
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
||||
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
||||
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
|
||||
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
||||
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
|
||||
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
|
||||
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
||||
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
|
||||
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
|
||||
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
|
||||
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
|
||||
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
|
||||
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
|
||||
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
||||
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
|
||||
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
||||
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||
|
||||
|
||||
#### Level logging
|
||||
|
||||
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||
|
||||
```go
|
||||
log.Debug("Useful debugging information.")
|
||||
log.Info("Something noteworthy happened!")
|
||||
log.Warn("You should probably take a look at this.")
|
||||
log.Error("Something failed but I'm not quitting.")
|
||||
// Calls os.Exit(1) after logging
|
||||
log.Fatal("Bye.")
|
||||
// Calls panic() after logging
|
||||
log.Panic("I'm bailing.")
|
||||
```
|
||||
|
||||
You can set the logging level on a `Logger`, then it will only log entries with
|
||||
that severity or anything above it:
|
||||
|
||||
```go
|
||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||
log.SetLevel(log.InfoLevel)
|
||||
```
|
||||
|
||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||
environment if your application has that.
|
||||
|
||||
#### Entries
|
||||
|
||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||
automatically added to all logging events:
|
||||
|
||||
1. `time`. The timestamp when the entry was created.
|
||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||
the `AddFields` call. E.g. `Failed to send event.`
|
||||
3. `level`. The logging level. E.g. `info`.
|
||||
|
||||
#### Environments
|
||||
|
||||
Logrus has no notion of environment.
|
||||
|
||||
If you wish for hooks and formatters to only be used in specific environments,
|
||||
you should handle that yourself. For example, if your application has a global
|
||||
variable `Environment`, which is a string representation of the environment you
|
||||
could do:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
init() {
|
||||
// do something here to set environment depending on an environment variable
|
||||
// or command-line flag
|
||||
if Environment == "production" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
} else {
|
||||
// The TextFormatter is default, you don't actually have to do this.
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration is how `logrus` was intended to be used, but JSON in
|
||||
production is mostly only useful if you do log aggregation with tools like
|
||||
Splunk or Logstash.
|
||||
|
||||
#### Formatters
|
||||
|
||||
The built-in logging formatters are:
|
||||
|
||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||
without colors.
|
||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||
field to `true`. To force no colored output even if there is a TTY set the
|
||||
`DisableColors` field to `true`
|
||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||
* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
|
||||
|
||||
```go
|
||||
logrus.SetFormatter(&logstash.LogstashFormatter{Type: "application_name"})
|
||||
```
|
||||
|
||||
Third party logging formatters:
|
||||
|
||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||
default ones (see Entries section above):
|
||||
|
||||
```go
|
||||
type MyJSONFormatter struct {
|
||||
}
|
||||
|
||||
log.SetFormatter(new(MyJSONFormatter))
|
||||
|
||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
// Note this doesn't include Time, Level and Message which are available on
|
||||
// the Entry. Consult `godoc` on information about those fields or read the
|
||||
// source of the official loggers.
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Logger as an `io.Writer`
|
||||
|
||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||
|
||||
```go
|
||||
w := logger.Writer()
|
||||
defer w.Close()
|
||||
|
||||
srv := http.Server{
|
||||
// create a stdlib log.Logger that writes to
|
||||
// logrus.Logger.
|
||||
ErrorLog: log.New(w, "", 0),
|
||||
}
|
||||
```
|
||||
|
||||
Each line written to that writer will be printed the usual way, using formatters
|
||||
and hooks. The level for those entries is `info`.
|
||||
|
||||
#### Rotation
|
||||
|
||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||
external program (like `logrotate(8)`) that can compress and delete old log
|
||||
entries. It should not be a feature of the application-level logger.
|
||||
|
||||
#### Tools
|
||||
|
||||
| Tool | Description |
|
||||
| ---- | ----------- |
|
||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||
|
||||
#### Testing
|
||||
|
||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||
|
||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||
|
||||
```go
|
||||
logger, hook := NewNullLogger()
|
||||
logger.Error("Hello error")
|
||||
|
||||
assert.Equal(1, len(hook.Entries))
|
||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(hook.LastEntry())
|
||||
```
|
|
@ -0,0 +1,64 @@
|
|||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://bitbucket.org/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
|
@ -3,11 +3,21 @@ package logrus
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bufferPool *sync.Pool
|
||||
|
||||
func init() {
|
||||
bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
|
@ -29,6 +39,9 @@ type Entry struct {
|
|||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
|
@ -39,21 +52,15 @@ func NewEntry(logger *Logger) *Entry {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
reader, err := entry.Reader()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return reader.String(), err
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
|
@ -81,6 +88,7 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
|||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
|
@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) {
|
|||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
reader, err := entry.Reader()
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
|
||||
_, err = io.Copy(entry.Logger.Out, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
|
@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
|||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
|
@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
|
@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
|||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
|
|
@ -31,18 +31,15 @@ type Formatter interface {
|
|||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
}
|
||||
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
}
|
||||
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,31 @@ type Logger struct {
|
|||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
}
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
|
@ -51,162 +74,235 @@ func New() *Logger {
|
|||
}
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
return NewEntry(logger).WithError(err)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debug(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Error(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatal(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panic(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infoln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
NewEntry(logger).Println(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
return true
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build solaris
|
||||
// +build solaris,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build windows,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ type TextFormatter struct {
|
|||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var b *bytes.Buffer
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
|
@ -65,8 +66,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
|||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
|
@ -118,7 +122,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
|||
}
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,34 +133,36 @@ func needsQuoting(text string) bool {
|
|||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if needsQuoting(value) {
|
||||
if !needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
case error:
|
||||
errmsg := value.Error()
|
||||
if needsQuoting(errmsg) {
|
||||
if !needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
fmt.Fprintf(b, "%q", errmsg)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
|
|
@ -7,18 +7,40 @@ import (
|
|||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
go logger.writerScanner(reader)
|
||||
var printFunc func(args ...interface{})
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = logger.Debug
|
||||
case InfoLevel:
|
||||
printFunc = logger.Info
|
||||
case WarnLevel:
|
||||
printFunc = logger.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = logger.Error
|
||||
case FatalLevel:
|
||||
printFunc = logger.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = logger.Panic
|
||||
default:
|
||||
printFunc = logger.Print
|
||||
}
|
||||
|
||||
go logger.writerScanner(reader, printFunc)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
logger.Print(scanner.Text())
|
||||
printFunc(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
[![Build Status](https://travis-ci.org/cloudfoundry-incubator/candiedyaml.svg)](https://travis-ci.org/cloudfoundry-incubator/candiedyaml)
|
||||
|
||||
candiedyaml
|
||||
===========
|
||||
|
||||
YAML for Go
|
||||
|
||||
A YAML 1.1 parser with support for YAML 1.2 features
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```go
|
||||
package myApp
|
||||
|
||||
import (
|
||||
"github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := os.Open("path/to/some/file.yml")
|
||||
if err != nil {
|
||||
println("File does not exist:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
document := new(interface{})
|
||||
decoder := candiedyaml.NewDecoder(file)
|
||||
err = decoder.Decode(document)
|
||||
|
||||
if err != nil {
|
||||
println("Failed to decode document:", err.Error())
|
||||
}
|
||||
|
||||
println("parsed yml into interface:", fmt.Sprintf("%#v", document))
|
||||
|
||||
fileToWrite, err := os.Create("path/to/some/new/file.yml")
|
||||
if err != nil {
|
||||
println("Failed to open file for writing:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
defer fileToWrite.Close()
|
||||
|
||||
encoder := candiedyaml.NewEncoder(fileToWrite)
|
||||
err = encoder.Encode(document)
|
||||
|
||||
if err != nil {
|
||||
println("Failed to encode document:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
```
|
|
@ -1,834 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
* Create a new parser object.
|
||||
*/
|
||||
|
||||
func yaml_parser_initialize(parser *yaml_parser_t) bool {
|
||||
*parser = yaml_parser_t{
|
||||
raw_buffer: make([]byte, 0, INPUT_RAW_BUFFER_SIZE),
|
||||
buffer: make([]byte, 0, INPUT_BUFFER_SIZE),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a parser object.
|
||||
*/
|
||||
func yaml_parser_delete(parser *yaml_parser_t) {
|
||||
*parser = yaml_parser_t{}
|
||||
}
|
||||
|
||||
/*
|
||||
* String read handler.
|
||||
*/
|
||||
|
||||
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (int, error) {
|
||||
if parser.input_pos == len(parser.input) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n := copy(buffer, parser.input[parser.input_pos:])
|
||||
parser.input_pos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* File read handler.
|
||||
*/
|
||||
|
||||
func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (int, error) {
|
||||
return parser.input_reader.Read(buffer)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a string input.
|
||||
*/
|
||||
|
||||
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
|
||||
if parser.read_handler != nil {
|
||||
panic("input already set")
|
||||
}
|
||||
|
||||
parser.read_handler = yaml_string_read_handler
|
||||
|
||||
parser.input = input
|
||||
parser.input_pos = 0
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a reader input
|
||||
*/
|
||||
func yaml_parser_set_input_reader(parser *yaml_parser_t, reader io.Reader) {
|
||||
if parser.read_handler != nil {
|
||||
panic("input already set")
|
||||
}
|
||||
|
||||
parser.read_handler = yaml_file_read_handler
|
||||
parser.input_reader = reader
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a generic input.
|
||||
*/
|
||||
|
||||
func yaml_parser_set_input(parser *yaml_parser_t, handler yaml_read_handler_t) {
|
||||
if parser.read_handler != nil {
|
||||
panic("input already set")
|
||||
}
|
||||
|
||||
parser.read_handler = handler
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the source encoding.
|
||||
*/
|
||||
|
||||
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
||||
if parser.encoding != yaml_ANY_ENCODING {
|
||||
panic("encoding already set")
|
||||
}
|
||||
|
||||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new emitter object.
|
||||
*/
|
||||
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{
|
||||
buffer: make([]byte, OUTPUT_BUFFER_SIZE),
|
||||
raw_buffer: make([]byte, 0, OUTPUT_RAW_BUFFER_SIZE),
|
||||
states: make([]yaml_emitter_state_t, 0, INITIAL_STACK_SIZE),
|
||||
events: make([]yaml_event_t, 0, INITIAL_QUEUE_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func yaml_emitter_delete(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{}
|
||||
}
|
||||
|
||||
/*
|
||||
* String write handler.
|
||||
*/
|
||||
|
||||
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* File write handler.
|
||||
*/
|
||||
|
||||
func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
_, err := emitter.output_writer.Write(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a string output.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, buffer *[]byte) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("output already set")
|
||||
}
|
||||
|
||||
emitter.write_handler = yaml_string_write_handler
|
||||
emitter.output_buffer = buffer
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a file output.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("output already set")
|
||||
}
|
||||
|
||||
emitter.write_handler = yaml_writer_write_handler
|
||||
emitter.output_writer = w
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a generic output handler.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_output(emitter *yaml_emitter_t, handler yaml_write_handler_t) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("output already set")
|
||||
}
|
||||
|
||||
emitter.write_handler = handler
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the output encoding.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
|
||||
if emitter.encoding != yaml_ANY_ENCODING {
|
||||
panic("encoding already set")
|
||||
}
|
||||
|
||||
emitter.encoding = encoding
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the canonical output style.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
|
||||
emitter.canonical = canonical
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the indentation increment.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
|
||||
if indent < 2 || indent > 9 {
|
||||
indent = 2
|
||||
}
|
||||
emitter.best_indent = indent
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the preferred line width.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
|
||||
if width < 0 {
|
||||
width = -1
|
||||
}
|
||||
emitter.best_width = width
|
||||
}
|
||||
|
||||
/*
|
||||
* Set if unescaped non-ASCII characters are allowed.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
|
||||
emitter.unicode = unicode
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the preferred line break character.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
|
||||
emitter.line_break = line_break
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a token object.
|
||||
*/
|
||||
|
||||
// yaml_DECLARE(void)
|
||||
// yaml_token_delete(yaml_token_t *token)
|
||||
// {
|
||||
// assert(token); /* Non-NULL token object expected. */
|
||||
//
|
||||
// switch (token.type)
|
||||
// {
|
||||
// case yaml_TAG_DIRECTIVE_TOKEN:
|
||||
// yaml_free(token.data.tag_directive.handle);
|
||||
// yaml_free(token.data.tag_directive.prefix);
|
||||
// break;
|
||||
//
|
||||
// case yaml_ALIAS_TOKEN:
|
||||
// yaml_free(token.data.alias.value);
|
||||
// break;
|
||||
//
|
||||
// case yaml_ANCHOR_TOKEN:
|
||||
// yaml_free(token.data.anchor.value);
|
||||
// break;
|
||||
//
|
||||
// case yaml_TAG_TOKEN:
|
||||
// yaml_free(token.data.tag.handle);
|
||||
// yaml_free(token.data.tag.suffix);
|
||||
// break;
|
||||
//
|
||||
// case yaml_SCALAR_TOKEN:
|
||||
// yaml_free(token.data.scalar.value);
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// memset(token, 0, sizeof(yaml_token_t));
|
||||
// }
|
||||
|
||||
/*
|
||||
* Check if a string is a valid UTF-8 sequence.
|
||||
*
|
||||
* Check 'reader.c' for more details on UTF-8 encoding.
|
||||
*/
|
||||
|
||||
// static int
|
||||
// yaml_check_utf8(yaml_char_t *start, size_t length)
|
||||
// {
|
||||
// yaml_char_t *end = start+length;
|
||||
// yaml_char_t *pointer = start;
|
||||
//
|
||||
// while (pointer < end) {
|
||||
// unsigned char octet;
|
||||
// unsigned int width;
|
||||
// unsigned int value;
|
||||
// size_t k;
|
||||
//
|
||||
// octet = pointer[0];
|
||||
// width = (octet & 0x80) == 0x00 ? 1 :
|
||||
// (octet & 0xE0) == 0xC0 ? 2 :
|
||||
// (octet & 0xF0) == 0xE0 ? 3 :
|
||||
// (octet & 0xF8) == 0xF0 ? 4 : 0;
|
||||
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
|
||||
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
|
||||
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
|
||||
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
|
||||
// if (!width) return 0;
|
||||
// if (pointer+width > end) return 0;
|
||||
// for (k = 1; k < width; k ++) {
|
||||
// octet = pointer[k];
|
||||
// if ((octet & 0xC0) != 0x80) return 0;
|
||||
// value = (value << 6) + (octet & 0x3F);
|
||||
// }
|
||||
// if (!((width == 1) ||
|
||||
// (width == 2 && value >= 0x80) ||
|
||||
// (width == 3 && value >= 0x800) ||
|
||||
// (width == 4 && value >= 0x10000))) return 0;
|
||||
//
|
||||
// pointer += width;
|
||||
// }
|
||||
//
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
/*
|
||||
* Create STREAM-START.
|
||||
*/
|
||||
|
||||
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_STREAM_START_EVENT,
|
||||
encoding: encoding,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create STREAM-END.
|
||||
*/
|
||||
|
||||
func yaml_stream_end_event_initialize(event *yaml_event_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_STREAM_END_EVENT,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DOCUMENT-START.
|
||||
*/
|
||||
|
||||
func yaml_document_start_event_initialize(event *yaml_event_t,
|
||||
version_directive *yaml_version_directive_t,
|
||||
tag_directives []yaml_tag_directive_t,
|
||||
implicit bool) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_DOCUMENT_START_EVENT,
|
||||
version_directive: version_directive,
|
||||
tag_directives: tag_directives,
|
||||
implicit: implicit,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DOCUMENT-END.
|
||||
*/
|
||||
|
||||
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_DOCUMENT_END_EVENT,
|
||||
implicit: implicit,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ALIAS.
|
||||
*/
|
||||
|
||||
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_ALIAS_EVENT,
|
||||
anchor: anchor,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create SCALAR.
|
||||
*/
|
||||
|
||||
func yaml_scalar_event_initialize(event *yaml_event_t,
|
||||
anchor []byte, tag []byte,
|
||||
value []byte,
|
||||
plain_implicit bool, quoted_implicit bool,
|
||||
style yaml_scalar_style_t) {
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SCALAR_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
value: value,
|
||||
implicit: plain_implicit,
|
||||
quoted_implicit: quoted_implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create SEQUENCE-START.
|
||||
*/
|
||||
|
||||
func yaml_sequence_start_event_initialize(event *yaml_event_t,
|
||||
anchor []byte, tag []byte, implicit bool, style yaml_sequence_style_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create SEQUENCE-END.
|
||||
*/
|
||||
|
||||
func yaml_sequence_end_event_initialize(event *yaml_event_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_END_EVENT,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create MAPPING-START.
|
||||
*/
|
||||
|
||||
func yaml_mapping_start_event_initialize(event *yaml_event_t,
|
||||
anchor []byte, tag []byte, implicit bool, style yaml_mapping_style_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create MAPPING-END.
|
||||
*/
|
||||
|
||||
func yaml_mapping_end_event_initialize(event *yaml_event_t) {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_END_EVENT,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an event object.
|
||||
*/
|
||||
|
||||
func yaml_event_delete(event *yaml_event_t) {
|
||||
*event = yaml_event_t{}
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Create a document object.
|
||||
// */
|
||||
//
|
||||
// func yaml_document_initialize(document *yaml_document_t,
|
||||
// version_directive *yaml_version_directive_t,
|
||||
// tag_directives []yaml_tag_directive_t,
|
||||
// start_implicit, end_implicit bool) bool {
|
||||
//
|
||||
//
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
// struct {
|
||||
// yaml_node_t *start;
|
||||
// yaml_node_t *end;
|
||||
// yaml_node_t *top;
|
||||
// } nodes = { NULL, NULL, NULL };
|
||||
// yaml_version_directive_t *version_directive_copy = NULL;
|
||||
// struct {
|
||||
// yaml_tag_directive_t *start;
|
||||
// yaml_tag_directive_t *end;
|
||||
// yaml_tag_directive_t *top;
|
||||
// } tag_directives_copy = { NULL, NULL, NULL };
|
||||
// yaml_tag_directive_t value = { NULL, NULL };
|
||||
// YAML_mark_t mark = { 0, 0, 0 };
|
||||
//
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
// assert((tag_directives_start && tag_directives_end) ||
|
||||
// (tag_directives_start == tag_directives_end));
|
||||
// /* Valid tag directives are expected. */
|
||||
//
|
||||
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error;
|
||||
//
|
||||
// if (version_directive) {
|
||||
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t));
|
||||
// if (!version_directive_copy) goto error;
|
||||
// version_directive_copy.major = version_directive.major;
|
||||
// version_directive_copy.minor = version_directive.minor;
|
||||
// }
|
||||
//
|
||||
// if (tag_directives_start != tag_directives_end) {
|
||||
// yaml_tag_directive_t *tag_directive;
|
||||
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
|
||||
// goto error;
|
||||
// for (tag_directive = tag_directives_start;
|
||||
// tag_directive != tag_directives_end; tag_directive ++) {
|
||||
// assert(tag_directive.handle);
|
||||
// assert(tag_directive.prefix);
|
||||
// if (!yaml_check_utf8(tag_directive.handle,
|
||||
// strlen((char *)tag_directive.handle)))
|
||||
// goto error;
|
||||
// if (!yaml_check_utf8(tag_directive.prefix,
|
||||
// strlen((char *)tag_directive.prefix)))
|
||||
// goto error;
|
||||
// value.handle = yaml_strdup(tag_directive.handle);
|
||||
// value.prefix = yaml_strdup(tag_directive.prefix);
|
||||
// if (!value.handle || !value.prefix) goto error;
|
||||
// if (!PUSH(&context, tag_directives_copy, value))
|
||||
// goto error;
|
||||
// value.handle = NULL;
|
||||
// value.prefix = NULL;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
|
||||
// tag_directives_copy.start, tag_directives_copy.top,
|
||||
// start_implicit, end_implicit, mark, mark);
|
||||
//
|
||||
// return 1;
|
||||
//
|
||||
// error:
|
||||
// STACK_DEL(&context, nodes);
|
||||
// yaml_free(version_directive_copy);
|
||||
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
|
||||
// yaml_tag_directive_t value = POP(&context, tag_directives_copy);
|
||||
// yaml_free(value.handle);
|
||||
// yaml_free(value.prefix);
|
||||
// }
|
||||
// STACK_DEL(&context, tag_directives_copy);
|
||||
// yaml_free(value.handle);
|
||||
// yaml_free(value.prefix);
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Destroy a document object.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(void)
|
||||
// yaml_document_delete(document *yaml_document_t)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
// yaml_tag_directive_t *tag_directive;
|
||||
//
|
||||
// context.error = yaml_NO_ERROR; /* Eliminate a compliler warning. */
|
||||
//
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
//
|
||||
// while (!STACK_EMPTY(&context, document.nodes)) {
|
||||
// yaml_node_t node = POP(&context, document.nodes);
|
||||
// yaml_free(node.tag);
|
||||
// switch (node.type) {
|
||||
// case yaml_SCALAR_NODE:
|
||||
// yaml_free(node.data.scalar.value);
|
||||
// break;
|
||||
// case yaml_SEQUENCE_NODE:
|
||||
// STACK_DEL(&context, node.data.sequence.items);
|
||||
// break;
|
||||
// case yaml_MAPPING_NODE:
|
||||
// STACK_DEL(&context, node.data.mapping.pairs);
|
||||
// break;
|
||||
// default:
|
||||
// assert(0); /* Should not happen. */
|
||||
// }
|
||||
// }
|
||||
// STACK_DEL(&context, document.nodes);
|
||||
//
|
||||
// yaml_free(document.version_directive);
|
||||
// for (tag_directive = document.tag_directives.start;
|
||||
// tag_directive != document.tag_directives.end;
|
||||
// tag_directive++) {
|
||||
// yaml_free(tag_directive.handle);
|
||||
// yaml_free(tag_directive.prefix);
|
||||
// }
|
||||
// yaml_free(document.tag_directives.start);
|
||||
//
|
||||
// memset(document, 0, sizeof(yaml_document_t));
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get a document node.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(yaml_node_t *)
|
||||
// yaml_document_get_node(document *yaml_document_t, int index)
|
||||
// {
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
//
|
||||
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
|
||||
// return document.nodes.start + index - 1;
|
||||
// }
|
||||
// return NULL;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Get the root object.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(yaml_node_t *)
|
||||
// yaml_document_get_root_node(document *yaml_document_t)
|
||||
// {
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
//
|
||||
// if (document.nodes.top != document.nodes.start) {
|
||||
// return document.nodes.start;
|
||||
// }
|
||||
// return NULL;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Add a scalar node to a document.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_document_add_scalar(document *yaml_document_t,
|
||||
// yaml_char_t *tag, yaml_char_t *value, int length,
|
||||
// yaml_scalar_style_t style)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
// YAML_mark_t mark = { 0, 0, 0 };
|
||||
// yaml_char_t *tag_copy = NULL;
|
||||
// yaml_char_t *value_copy = NULL;
|
||||
// yaml_node_t node;
|
||||
//
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
// assert(value); /* Non-NULL value is expected. */
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)yaml_DEFAULT_SCALAR_TAG;
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
|
||||
// tag_copy = yaml_strdup(tag);
|
||||
// if (!tag_copy) goto error;
|
||||
//
|
||||
// if (length < 0) {
|
||||
// length = strlen((char *)value);
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(value, length)) goto error;
|
||||
// value_copy = yaml_malloc(length+1);
|
||||
// if (!value_copy) goto error;
|
||||
// memcpy(value_copy, value, length);
|
||||
// value_copy[length] = '\0';
|
||||
//
|
||||
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark);
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error;
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start;
|
||||
//
|
||||
// error:
|
||||
// yaml_free(tag_copy);
|
||||
// yaml_free(value_copy);
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Add a sequence node to a document.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_document_add_sequence(document *yaml_document_t,
|
||||
// yaml_char_t *tag, yaml_sequence_style_t style)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
// YAML_mark_t mark = { 0, 0, 0 };
|
||||
// yaml_char_t *tag_copy = NULL;
|
||||
// struct {
|
||||
// yaml_node_item_t *start;
|
||||
// yaml_node_item_t *end;
|
||||
// yaml_node_item_t *top;
|
||||
// } items = { NULL, NULL, NULL };
|
||||
// yaml_node_t node;
|
||||
//
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)yaml_DEFAULT_SEQUENCE_TAG;
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
|
||||
// tag_copy = yaml_strdup(tag);
|
||||
// if (!tag_copy) goto error;
|
||||
//
|
||||
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error;
|
||||
//
|
||||
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
|
||||
// style, mark, mark);
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error;
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start;
|
||||
//
|
||||
// error:
|
||||
// STACK_DEL(&context, items);
|
||||
// yaml_free(tag_copy);
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Add a mapping node to a document.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_document_add_mapping(document *yaml_document_t,
|
||||
// yaml_char_t *tag, yaml_mapping_style_t style)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
// YAML_mark_t mark = { 0, 0, 0 };
|
||||
// yaml_char_t *tag_copy = NULL;
|
||||
// struct {
|
||||
// yaml_node_pair_t *start;
|
||||
// yaml_node_pair_t *end;
|
||||
// yaml_node_pair_t *top;
|
||||
// } pairs = { NULL, NULL, NULL };
|
||||
// yaml_node_t node;
|
||||
//
|
||||
// assert(document); /* Non-NULL document object is expected. */
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)yaml_DEFAULT_MAPPING_TAG;
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error;
|
||||
// tag_copy = yaml_strdup(tag);
|
||||
// if (!tag_copy) goto error;
|
||||
//
|
||||
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error;
|
||||
//
|
||||
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
|
||||
// style, mark, mark);
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error;
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start;
|
||||
//
|
||||
// error:
|
||||
// STACK_DEL(&context, pairs);
|
||||
// yaml_free(tag_copy);
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Append an item to a sequence node.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_document_append_sequence_item(document *yaml_document_t,
|
||||
// int sequence, int item)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
//
|
||||
// assert(document); /* Non-NULL document is required. */
|
||||
// assert(sequence > 0
|
||||
// && document.nodes.start + sequence <= document.nodes.top);
|
||||
// /* Valid sequence id is required. */
|
||||
// assert(document.nodes.start[sequence-1].type == yaml_SEQUENCE_NODE);
|
||||
// /* A sequence node is required. */
|
||||
// assert(item > 0 && document.nodes.start + item <= document.nodes.top);
|
||||
// /* Valid item id is required. */
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[sequence-1].data.sequence.items, item))
|
||||
// return 0;
|
||||
//
|
||||
// return 1;
|
||||
// }
|
||||
//
|
||||
// /*
|
||||
// * Append a pair of a key and a value to a mapping node.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_document_append_mapping_pair(document *yaml_document_t,
|
||||
// int mapping, int key, int value)
|
||||
// {
|
||||
// struct {
|
||||
// YAML_error_type_t error;
|
||||
// } context;
|
||||
//
|
||||
// yaml_node_pair_t pair;
|
||||
//
|
||||
// assert(document); /* Non-NULL document is required. */
|
||||
// assert(mapping > 0
|
||||
// && document.nodes.start + mapping <= document.nodes.top);
|
||||
// /* Valid mapping id is required. */
|
||||
// assert(document.nodes.start[mapping-1].type == yaml_MAPPING_NODE);
|
||||
// /* A mapping node is required. */
|
||||
// assert(key > 0 && document.nodes.start + key <= document.nodes.top);
|
||||
// /* Valid key id is required. */
|
||||
// assert(value > 0 && document.nodes.start + value <= document.nodes.top);
|
||||
// /* Valid value id is required. */
|
||||
//
|
||||
// pair.key = key;
|
||||
// pair.value = value;
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
|
||||
// return 0;
|
||||
//
|
||||
// return 1;
|
||||
// }
|
||||
//
|
|
@ -1,622 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Unmarshaler interface {
|
||||
UnmarshalYAML(tag string, value interface{}) error
|
||||
}
|
||||
|
||||
// A Number represents a JSON number literal.
|
||||
type Number string
|
||||
|
||||
// String returns the literal text of the number.
|
||||
func (n Number) String() string { return string(n) }
|
||||
|
||||
// Float64 returns the number as a float64.
|
||||
func (n Number) Float64() (float64, error) {
|
||||
return strconv.ParseFloat(string(n), 64)
|
||||
}
|
||||
|
||||
// Int64 returns the number as an int64.
|
||||
func (n Number) Int64() (int64, error) {
|
||||
return strconv.ParseInt(string(n), 10, 64)
|
||||
}
|
||||
|
||||
type Decoder struct {
|
||||
parser yaml_parser_t
|
||||
event yaml_event_t
|
||||
replay_events []yaml_event_t
|
||||
useNumber bool
|
||||
|
||||
anchors map[string][]yaml_event_t
|
||||
tracking_anchors [][]yaml_event_t
|
||||
}
|
||||
|
||||
type ParserError struct {
|
||||
ErrorType YAML_error_type_t
|
||||
Context string
|
||||
ContextMark YAML_mark_t
|
||||
Problem string
|
||||
ProblemMark YAML_mark_t
|
||||
}
|
||||
|
||||
func (e *ParserError) Error() string {
|
||||
return fmt.Sprintf("yaml: [%s] %s at line %d, column %d", e.Context, e.Problem, e.ProblemMark.line+1, e.ProblemMark.column+1)
|
||||
}
|
||||
|
||||
type UnexpectedEventError struct {
|
||||
Value string
|
||||
EventType yaml_event_type_t
|
||||
At YAML_mark_t
|
||||
}
|
||||
|
||||
func (e *UnexpectedEventError) Error() string {
|
||||
return fmt.Sprintf("yaml: Unexpect event [%d]: '%s' at line %d, column %d", e.EventType, e.Value, e.At.line+1, e.At.column+1)
|
||||
}
|
||||
|
||||
func recovery(err *error) {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
panic(r)
|
||||
}
|
||||
|
||||
var tmpError error
|
||||
switch r := r.(type) {
|
||||
case error:
|
||||
tmpError = r
|
||||
case string:
|
||||
tmpError = errors.New(r)
|
||||
default:
|
||||
tmpError = errors.New("Unknown panic: " + reflect.ValueOf(r).String())
|
||||
}
|
||||
|
||||
*err = tmpError
|
||||
}
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
d := NewDecoder(bytes.NewBuffer(data))
|
||||
return d.Decode(v)
|
||||
}
|
||||
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
d := &Decoder{
|
||||
anchors: make(map[string][]yaml_event_t),
|
||||
tracking_anchors: make([][]yaml_event_t, 1),
|
||||
}
|
||||
yaml_parser_initialize(&d.parser)
|
||||
yaml_parser_set_input_reader(&d.parser, r)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Decoder) Decode(v interface{}) (err error) {
|
||||
defer recovery(&err)
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||
return fmt.Errorf("Expected a pointer or nil but was a %s at %s", rv.String(), d.event.start_mark)
|
||||
}
|
||||
|
||||
if d.event.event_type == yaml_NO_EVENT {
|
||||
d.nextEvent()
|
||||
|
||||
if d.event.event_type != yaml_STREAM_START_EVENT {
|
||||
return errors.New("Invalid stream")
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
d.document(rv)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) UseNumber() { d.useNumber = true }
|
||||
|
||||
func (d *Decoder) error(err error) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func (d *Decoder) nextEvent() {
|
||||
if d.event.event_type == yaml_STREAM_END_EVENT {
|
||||
d.error(errors.New("The stream is closed"))
|
||||
}
|
||||
|
||||
if d.replay_events != nil {
|
||||
d.event = d.replay_events[0]
|
||||
if len(d.replay_events) == 1 {
|
||||
d.replay_events = nil
|
||||
} else {
|
||||
d.replay_events = d.replay_events[1:]
|
||||
}
|
||||
} else {
|
||||
if !yaml_parser_parse(&d.parser, &d.event) {
|
||||
yaml_event_delete(&d.event)
|
||||
|
||||
d.error(&ParserError{
|
||||
ErrorType: d.parser.error,
|
||||
Context: d.parser.context,
|
||||
ContextMark: d.parser.context_mark,
|
||||
Problem: d.parser.problem,
|
||||
ProblemMark: d.parser.problem_mark,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
last := len(d.tracking_anchors)
|
||||
// skip aliases when tracking an anchor
|
||||
if last > 0 && d.event.event_type != yaml_ALIAS_EVENT {
|
||||
d.tracking_anchors[last-1] = append(d.tracking_anchors[last-1], d.event)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) document(rv reflect.Value) {
|
||||
if d.event.event_type != yaml_DOCUMENT_START_EVENT {
|
||||
d.error(fmt.Errorf("Expected document start at %s", d.event.start_mark))
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
d.parse(rv)
|
||||
|
||||
if d.event.event_type != yaml_DOCUMENT_END_EVENT {
|
||||
d.error(fmt.Errorf("Expected document end at %s", d.event.start_mark))
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
func (d *Decoder) parse(rv reflect.Value) {
|
||||
if !rv.IsValid() {
|
||||
// skip ahead since we cannot store
|
||||
d.valueInterface()
|
||||
return
|
||||
}
|
||||
|
||||
anchor := string(d.event.anchor)
|
||||
switch d.event.event_type {
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
d.sequence(rv)
|
||||
d.end_anchor(anchor)
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
d.mapping(rv)
|
||||
d.end_anchor(anchor)
|
||||
case yaml_SCALAR_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
d.scalar(rv)
|
||||
d.end_anchor(anchor)
|
||||
case yaml_ALIAS_EVENT:
|
||||
d.alias(rv)
|
||||
case yaml_DOCUMENT_END_EVENT:
|
||||
default:
|
||||
d.error(&UnexpectedEventError{
|
||||
Value: string(d.event.value),
|
||||
EventType: d.event.event_type,
|
||||
At: d.event.start_mark,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) begin_anchor(anchor string) {
|
||||
if anchor != "" {
|
||||
events := []yaml_event_t{d.event}
|
||||
d.tracking_anchors = append(d.tracking_anchors, events)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) end_anchor(anchor string) {
|
||||
if anchor != "" {
|
||||
events := d.tracking_anchors[len(d.tracking_anchors)-1]
|
||||
d.tracking_anchors = d.tracking_anchors[0 : len(d.tracking_anchors)-1]
|
||||
// remove the anchor, replaying events shouldn't have anchors
|
||||
events[0].anchor = nil
|
||||
// we went one too many, remove the extra event
|
||||
events = events[:len(events)-1]
|
||||
// if nested, append to all the other anchors
|
||||
for i, e := range d.tracking_anchors {
|
||||
d.tracking_anchors[i] = append(e, events...)
|
||||
}
|
||||
d.anchors[anchor] = events
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
|
||||
// If v is a named type and is addressable,
|
||||
// start with its address, so that if the type has pointer methods,
|
||||
// we find them.
|
||||
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
for {
|
||||
// Load value from interface, but only if the result will be
|
||||
// usefully addressable.
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
e := v.Elem()
|
||||
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
|
||||
v = e
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Ptr {
|
||||
break
|
||||
}
|
||||
|
||||
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
|
||||
break
|
||||
}
|
||||
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
|
||||
if v.Type().NumMethod() > 0 {
|
||||
if u, ok := v.Interface().(Unmarshaler); ok {
|
||||
var temp interface{}
|
||||
return u, reflect.ValueOf(&temp)
|
||||
}
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return nil, v
|
||||
}
|
||||
|
||||
func (d *Decoder) sequence(v reflect.Value) {
|
||||
if d.event.event_type != yaml_SEQUENCE_START_EVENT {
|
||||
d.error(fmt.Errorf("Expected sequence start at %s", d.event.start_mark))
|
||||
}
|
||||
|
||||
u, pv := d.indirect(v, false)
|
||||
if u != nil {
|
||||
defer func() {
|
||||
if err := u.UnmarshalYAML(yaml_SEQ_TAG, pv.Interface()); err != nil {
|
||||
d.error(err)
|
||||
}
|
||||
}()
|
||||
_, pv = d.indirect(pv, false)
|
||||
}
|
||||
|
||||
v = pv
|
||||
|
||||
// Check type of target.
|
||||
switch v.Kind() {
|
||||
case reflect.Interface:
|
||||
if v.NumMethod() == 0 {
|
||||
// Decoding into nil interface? Switch to non-reflect code.
|
||||
v.Set(reflect.ValueOf(d.sequenceInterface()))
|
||||
return
|
||||
}
|
||||
// Otherwise it's invalid.
|
||||
fallthrough
|
||||
default:
|
||||
d.error(fmt.Errorf("Expected an array, slice or interface{} but was a %s at %s", v, d.event.start_mark))
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
break
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
|
||||
i := 0
|
||||
done:
|
||||
for {
|
||||
switch d.event.event_type {
|
||||
case yaml_SEQUENCE_END_EVENT, yaml_DOCUMENT_END_EVENT:
|
||||
break done
|
||||
}
|
||||
|
||||
// Get element of array, growing if necessary.
|
||||
if v.Kind() == reflect.Slice {
|
||||
// Grow slice if necessary
|
||||
if i >= v.Cap() {
|
||||
newcap := v.Cap() + v.Cap()/2
|
||||
if newcap < 4 {
|
||||
newcap = 4
|
||||
}
|
||||
newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
|
||||
reflect.Copy(newv, v)
|
||||
v.Set(newv)
|
||||
}
|
||||
if i >= v.Len() {
|
||||
v.SetLen(i + 1)
|
||||
}
|
||||
}
|
||||
|
||||
if i < v.Len() {
|
||||
// Decode into element.
|
||||
d.parse(v.Index(i))
|
||||
} else {
|
||||
// Ran out of fixed array: skip.
|
||||
d.parse(reflect.Value{})
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if i < v.Len() {
|
||||
if v.Kind() == reflect.Array {
|
||||
// Array. Zero the rest.
|
||||
z := reflect.Zero(v.Type().Elem())
|
||||
for ; i < v.Len(); i++ {
|
||||
v.Index(i).Set(z)
|
||||
}
|
||||
} else {
|
||||
v.SetLen(i)
|
||||
}
|
||||
}
|
||||
if i == 0 && v.Kind() == reflect.Slice {
|
||||
v.Set(reflect.MakeSlice(v.Type(), 0, 0))
|
||||
}
|
||||
|
||||
if d.event.event_type != yaml_DOCUMENT_END_EVENT {
|
||||
d.nextEvent()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) mapping(v reflect.Value) {
|
||||
u, pv := d.indirect(v, false)
|
||||
if u != nil {
|
||||
defer func() {
|
||||
if err := u.UnmarshalYAML(yaml_MAP_TAG, pv.Interface()); err != nil {
|
||||
d.error(err)
|
||||
}
|
||||
}()
|
||||
_, pv = d.indirect(pv, false)
|
||||
}
|
||||
v = pv
|
||||
|
||||
// Decoding into nil interface? Switch to non-reflect code.
|
||||
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
|
||||
v.Set(reflect.ValueOf(d.mappingInterface()))
|
||||
return
|
||||
}
|
||||
|
||||
// Check type of target: struct or map[X]Y
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
d.mappingStruct(v)
|
||||
return
|
||||
case reflect.Map:
|
||||
default:
|
||||
d.error(fmt.Errorf("Expected a struct or map but was a %s at %s ", v, d.event.start_mark))
|
||||
}
|
||||
|
||||
mapt := v.Type()
|
||||
if v.IsNil() {
|
||||
v.Set(reflect.MakeMap(mapt))
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
|
||||
keyt := mapt.Key()
|
||||
mapElemt := mapt.Elem()
|
||||
|
||||
var mapElem reflect.Value
|
||||
done:
|
||||
for {
|
||||
switch d.event.event_type {
|
||||
case yaml_MAPPING_END_EVENT:
|
||||
break done
|
||||
case yaml_DOCUMENT_END_EVENT:
|
||||
return
|
||||
}
|
||||
|
||||
key := reflect.New(keyt)
|
||||
d.parse(key.Elem())
|
||||
|
||||
if !mapElem.IsValid() {
|
||||
mapElem = reflect.New(mapElemt).Elem()
|
||||
} else {
|
||||
mapElem.Set(reflect.Zero(mapElemt))
|
||||
}
|
||||
|
||||
d.parse(mapElem)
|
||||
|
||||
v.SetMapIndex(key.Elem(), mapElem)
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
func (d *Decoder) mappingStruct(v reflect.Value) {
|
||||
|
||||
structt := v.Type()
|
||||
fields := cachedTypeFields(structt)
|
||||
|
||||
d.nextEvent()
|
||||
|
||||
done:
|
||||
for {
|
||||
switch d.event.event_type {
|
||||
case yaml_MAPPING_END_EVENT:
|
||||
break done
|
||||
case yaml_DOCUMENT_END_EVENT:
|
||||
return
|
||||
}
|
||||
|
||||
key := ""
|
||||
d.parse(reflect.ValueOf(&key))
|
||||
|
||||
// Figure out field corresponding to key.
|
||||
var subv reflect.Value
|
||||
|
||||
var f *field
|
||||
for i := range fields {
|
||||
ff := &fields[i]
|
||||
if ff.name == key {
|
||||
f = ff
|
||||
break
|
||||
}
|
||||
|
||||
if f == nil && strings.EqualFold(ff.name, key) {
|
||||
f = ff
|
||||
}
|
||||
}
|
||||
|
||||
if f != nil {
|
||||
subv = v
|
||||
for _, i := range f.index {
|
||||
if subv.Kind() == reflect.Ptr {
|
||||
if subv.IsNil() {
|
||||
subv.Set(reflect.New(subv.Type().Elem()))
|
||||
}
|
||||
subv = subv.Elem()
|
||||
}
|
||||
subv = subv.Field(i)
|
||||
}
|
||||
}
|
||||
d.parse(subv)
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
func (d *Decoder) scalar(v reflect.Value) {
|
||||
val := string(d.event.value)
|
||||
wantptr := null_values[val]
|
||||
|
||||
u, pv := d.indirect(v, wantptr)
|
||||
|
||||
var tag string
|
||||
if u != nil {
|
||||
defer func() {
|
||||
if err := u.UnmarshalYAML(tag, pv.Interface()); err != nil {
|
||||
d.error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, pv = d.indirect(pv, wantptr)
|
||||
}
|
||||
v = pv
|
||||
|
||||
var err error
|
||||
tag, err = resolve(d.event, v, d.useNumber)
|
||||
if err != nil {
|
||||
d.error(err)
|
||||
}
|
||||
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
func (d *Decoder) alias(rv reflect.Value) {
|
||||
val, ok := d.anchors[string(d.event.anchor)]
|
||||
if !ok {
|
||||
d.error(fmt.Errorf("missing anchor: '%s' at %s", d.event.anchor, d.event.start_mark))
|
||||
}
|
||||
|
||||
d.replay_events = val
|
||||
d.nextEvent()
|
||||
d.parse(rv)
|
||||
}
|
||||
|
||||
func (d *Decoder) valueInterface() interface{} {
|
||||
var v interface{}
|
||||
|
||||
anchor := string(d.event.anchor)
|
||||
switch d.event.event_type {
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
v = d.sequenceInterface()
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
v = d.mappingInterface()
|
||||
case yaml_SCALAR_EVENT:
|
||||
d.begin_anchor(anchor)
|
||||
v = d.scalarInterface()
|
||||
case yaml_ALIAS_EVENT:
|
||||
rv := reflect.ValueOf(&v)
|
||||
d.alias(rv)
|
||||
return v
|
||||
case yaml_DOCUMENT_END_EVENT:
|
||||
d.error(&UnexpectedEventError{
|
||||
Value: string(d.event.value),
|
||||
EventType: d.event.event_type,
|
||||
At: d.event.start_mark,
|
||||
})
|
||||
|
||||
}
|
||||
d.end_anchor(anchor)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (d *Decoder) scalarInterface() interface{} {
|
||||
_, v := resolveInterface(d.event, d.useNumber)
|
||||
|
||||
d.nextEvent()
|
||||
return v
|
||||
}
|
||||
|
||||
// sequenceInterface is like sequence but returns []interface{}.
|
||||
func (d *Decoder) sequenceInterface() []interface{} {
|
||||
var v = make([]interface{}, 0)
|
||||
|
||||
d.nextEvent()
|
||||
|
||||
done:
|
||||
for {
|
||||
switch d.event.event_type {
|
||||
case yaml_SEQUENCE_END_EVENT, yaml_DOCUMENT_END_EVENT:
|
||||
break done
|
||||
}
|
||||
|
||||
v = append(v, d.valueInterface())
|
||||
}
|
||||
|
||||
if d.event.event_type != yaml_DOCUMENT_END_EVENT {
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// mappingInterface is like mapping but returns map[interface{}]interface{}.
|
||||
func (d *Decoder) mappingInterface() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
|
||||
d.nextEvent()
|
||||
|
||||
done:
|
||||
for {
|
||||
switch d.event.event_type {
|
||||
case yaml_MAPPING_END_EVENT, yaml_DOCUMENT_END_EVENT:
|
||||
break done
|
||||
}
|
||||
|
||||
key := d.valueInterface()
|
||||
|
||||
// Read value.
|
||||
m[key] = d.valueInterface()
|
||||
}
|
||||
|
||||
if d.event.event_type != yaml_DOCUMENT_END_EVENT {
|
||||
d.nextEvent()
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
|
@ -1,2072 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
var default_tag_directives = []yaml_tag_directive_t{
|
||||
{[]byte("!"), []byte("!")},
|
||||
{[]byte("!!"), []byte("tag:yaml.org,2002:")},
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the buffer if needed.
|
||||
*/
|
||||
|
||||
func flush(emitter *yaml_emitter_t) bool {
|
||||
if emitter.buffer_pos+5 >= len(emitter.buffer) {
|
||||
return yaml_emitter_flush(emitter)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a character to the output buffer.
|
||||
*/
|
||||
func put(emitter *yaml_emitter_t, value byte) bool {
|
||||
if !flush(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.buffer[emitter.buffer_pos] = value
|
||||
emitter.buffer_pos++
|
||||
emitter.column++
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a line break to the output buffer.
|
||||
*/
|
||||
|
||||
func put_break(emitter *yaml_emitter_t) bool {
|
||||
if !flush(emitter) {
|
||||
return false
|
||||
}
|
||||
switch emitter.line_break {
|
||||
case yaml_CR_BREAK:
|
||||
emitter.buffer[emitter.buffer_pos] = '\r'
|
||||
emitter.buffer_pos++
|
||||
case yaml_LN_BREAK:
|
||||
emitter.buffer[emitter.buffer_pos] = '\n'
|
||||
emitter.buffer_pos++
|
||||
case yaml_CRLN_BREAK:
|
||||
emitter.buffer[emitter.buffer_pos] = '\r'
|
||||
emitter.buffer[emitter.buffer_pos] = '\n'
|
||||
emitter.buffer_pos += 2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
emitter.column = 0
|
||||
emitter.line++
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a character from a string into buffer.
|
||||
*/
|
||||
func write(emitter *yaml_emitter_t, src []byte, src_pos *int) bool {
|
||||
if !flush(emitter) {
|
||||
return false
|
||||
}
|
||||
copy_bytes(emitter.buffer, &emitter.buffer_pos, src, src_pos)
|
||||
emitter.column++
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a line break character from a string into buffer.
|
||||
*/
|
||||
|
||||
func write_break(emitter *yaml_emitter_t, src []byte, src_pos *int) bool {
|
||||
if src[*src_pos] == '\n' {
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
*src_pos++
|
||||
} else {
|
||||
if !write(emitter, src, src_pos) {
|
||||
return false
|
||||
}
|
||||
emitter.column = 0
|
||||
emitter.line++
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an emitter error and return 0.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool {
|
||||
emitter.error = yaml_EMITTER_ERROR
|
||||
emitter.problem = problem
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an event.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
emitter.events = append(emitter.events, *event)
|
||||
for !yaml_emitter_need_more_events(emitter) {
|
||||
event := &emitter.events[emitter.events_head]
|
||||
if !yaml_emitter_analyze_event(emitter, event) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_state_machine(emitter, event) {
|
||||
return false
|
||||
}
|
||||
yaml_event_delete(event)
|
||||
emitter.events_head++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we need to accumulate more events before emitting.
|
||||
*
|
||||
* We accumulate extra
|
||||
* - 1 event for DOCUMENT-START
|
||||
* - 2 events for SEQUENCE-START
|
||||
* - 3 events for MAPPING-START
|
||||
*/
|
||||
|
||||
func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
|
||||
if emitter.events_head == len(emitter.events) {
|
||||
return true
|
||||
}
|
||||
|
||||
accumulate := 0
|
||||
switch emitter.events[emitter.events_head].event_type {
|
||||
case yaml_DOCUMENT_START_EVENT:
|
||||
accumulate = 1
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
accumulate = 2
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
accumulate = 3
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if len(emitter.events)-emitter.events_head > accumulate {
|
||||
return false
|
||||
}
|
||||
|
||||
level := 0
|
||||
for i := emitter.events_head; i < len(emitter.events); i++ {
|
||||
switch emitter.events[i].event_type {
|
||||
case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT:
|
||||
level++
|
||||
case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT:
|
||||
level--
|
||||
}
|
||||
|
||||
if level == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Append a directive to the directives stack.
|
||||
*/
|
||||
|
||||
func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t,
|
||||
value *yaml_tag_directive_t, allow_duplicates bool) bool {
|
||||
|
||||
for i := range emitter.tag_directives {
|
||||
|
||||
if bytes.Equal(value.handle, emitter.tag_directives[i].handle) {
|
||||
if allow_duplicates {
|
||||
return true
|
||||
}
|
||||
return yaml_emitter_set_emitter_error(emitter, "duplicat %TAG directive")
|
||||
}
|
||||
}
|
||||
|
||||
tag_copy := yaml_tag_directive_t{
|
||||
handle: value.handle,
|
||||
prefix: value.prefix,
|
||||
}
|
||||
|
||||
emitter.tag_directives = append(emitter.tag_directives, tag_copy)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase the indentation level.
|
||||
*/
|
||||
|
||||
func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow bool, indentless bool) bool {
|
||||
|
||||
emitter.indents = append(emitter.indents, emitter.indent)
|
||||
|
||||
if emitter.indent < 0 {
|
||||
if flow {
|
||||
emitter.indent = emitter.best_indent
|
||||
} else {
|
||||
emitter.indent = 0
|
||||
}
|
||||
} else if !indentless {
|
||||
emitter.indent += emitter.best_indent
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* State dispatcher.
|
||||
*/
|
||||
|
||||
func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
switch emitter.state {
|
||||
case yaml_EMIT_STREAM_START_STATE:
|
||||
return yaml_emitter_emit_stream_start(emitter, event)
|
||||
|
||||
case yaml_EMIT_FIRST_DOCUMENT_START_STATE:
|
||||
return yaml_emitter_emit_document_start(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_DOCUMENT_START_STATE:
|
||||
return yaml_emitter_emit_document_start(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_DOCUMENT_CONTENT_STATE:
|
||||
return yaml_emitter_emit_document_content(emitter, event)
|
||||
|
||||
case yaml_EMIT_DOCUMENT_END_STATE:
|
||||
return yaml_emitter_emit_document_end(emitter, event)
|
||||
|
||||
case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
|
||||
return yaml_emitter_emit_flow_sequence_item(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
|
||||
return yaml_emitter_emit_flow_sequence_item(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
|
||||
return yaml_emitter_emit_flow_mapping_key(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
|
||||
return yaml_emitter_emit_flow_mapping_key(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
|
||||
return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_FLOW_MAPPING_VALUE_STATE:
|
||||
return yaml_emitter_emit_flow_mapping_value(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
|
||||
return yaml_emitter_emit_block_sequence_item(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
|
||||
return yaml_emitter_emit_block_sequence_item(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
|
||||
return yaml_emitter_emit_block_mapping_key(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_BLOCK_MAPPING_KEY_STATE:
|
||||
return yaml_emitter_emit_block_mapping_key(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
|
||||
return yaml_emitter_emit_block_mapping_value(emitter, event, true)
|
||||
|
||||
case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE:
|
||||
return yaml_emitter_emit_block_mapping_value(emitter, event, false)
|
||||
|
||||
case yaml_EMIT_END_STATE:
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"expected nothing after STREAM-END")
|
||||
|
||||
}
|
||||
|
||||
panic("invalid state")
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect STREAM-START.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
|
||||
if event.event_type != yaml_STREAM_START_EVENT {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"expected STREAM-START")
|
||||
}
|
||||
|
||||
if emitter.encoding == yaml_ANY_ENCODING {
|
||||
emitter.encoding = event.encoding
|
||||
|
||||
if emitter.encoding == yaml_ANY_ENCODING {
|
||||
emitter.encoding = yaml_UTF8_ENCODING
|
||||
}
|
||||
}
|
||||
|
||||
if emitter.best_indent < 2 || emitter.best_indent > 9 {
|
||||
emitter.best_indent = 2
|
||||
}
|
||||
|
||||
if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 {
|
||||
emitter.best_width = 80
|
||||
}
|
||||
|
||||
if emitter.best_width < 0 {
|
||||
emitter.best_width = 1<<31 - 1
|
||||
}
|
||||
|
||||
if emitter.line_break == yaml_ANY_BREAK {
|
||||
emitter.line_break = yaml_LN_BREAK
|
||||
}
|
||||
|
||||
emitter.indent = -1
|
||||
|
||||
emitter.line = 0
|
||||
emitter.column = 0
|
||||
emitter.whitespace = true
|
||||
emitter.indention = true
|
||||
|
||||
if emitter.encoding != yaml_UTF8_ENCODING {
|
||||
if !yaml_emitter_write_bom(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect DOCUMENT-START or STREAM-END.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_document_start(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
|
||||
if event.event_type == yaml_DOCUMENT_START_EVENT {
|
||||
if event.version_directive != nil {
|
||||
if !yaml_emitter_analyze_version_directive(emitter,
|
||||
*event.version_directive) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := range event.tag_directives {
|
||||
tag_directive := &event.tag_directives[i]
|
||||
|
||||
if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := range default_tag_directives {
|
||||
if !yaml_emitter_append_tag_directive(emitter, &default_tag_directives[i], true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
implicit := event.implicit
|
||||
if !first || emitter.canonical {
|
||||
implicit = false
|
||||
}
|
||||
|
||||
if (event.version_directive != nil || len(event.tag_directives) > 0) &&
|
||||
emitter.open_ended {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if event.version_directive != nil {
|
||||
implicit = false
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(event.tag_directives) > 0 {
|
||||
implicit = false
|
||||
for i := range event.tag_directives {
|
||||
tag_directive := &event.tag_directives[i]
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if yaml_emitter_check_empty_document(emitter) {
|
||||
implicit = false
|
||||
}
|
||||
|
||||
if !implicit {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if emitter.canonical {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
|
||||
|
||||
return true
|
||||
} else if event.event_type == yaml_STREAM_END_EVENT {
|
||||
if emitter.open_ended {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !yaml_emitter_flush(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.state = yaml_EMIT_END_STATE
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"expected DOCUMENT-START or STREAM-END")
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect the root node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
|
||||
|
||||
return yaml_emitter_emit_node(emitter, event, true, false, false, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect DOCUMENT-END.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
|
||||
if event.event_type != yaml_DOCUMENT_END_EVENT {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"expected DOCUMENT-END")
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
if !event.implicit {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !yaml_emitter_flush(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.state = yaml_EMIT_DOCUMENT_START_STATE
|
||||
emitter.tag_directives = emitter.tag_directives[:0]
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Expect a flow item node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("["), true, true, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_increase_indent(emitter, true, false) {
|
||||
return false
|
||||
}
|
||||
emitter.flow_level++
|
||||
}
|
||||
|
||||
if event.event_type == yaml_SEQUENCE_END_EVENT {
|
||||
emitter.flow_level--
|
||||
emitter.indent = emitter.indents[len(emitter.indents)-1]
|
||||
emitter.indents = emitter.indents[:len(emitter.indents)-1]
|
||||
if emitter.canonical && !first {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(","), false, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("]"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if !first {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(","), false, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if emitter.canonical || emitter.column > emitter.best_width {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
|
||||
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a flow key node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
|
||||
if first {
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("{"), true, true, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_increase_indent(emitter, true, false) {
|
||||
return false
|
||||
}
|
||||
emitter.flow_level++
|
||||
}
|
||||
|
||||
if event.event_type == yaml_MAPPING_END_EVENT {
|
||||
emitter.flow_level--
|
||||
emitter.indent = emitter.indents[len(emitter.indents)-1]
|
||||
emitter.indents = emitter.indents[:len(emitter.indents)-1]
|
||||
|
||||
if emitter.canonical && !first {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(","), false, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("}"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if !first {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(","), false, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if emitter.canonical || emitter.column > emitter.best_width {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if !emitter.canonical && yaml_emitter_check_simple_key(emitter) {
|
||||
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE)
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, true)
|
||||
} else {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("?"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE)
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a flow value node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, simple bool) bool {
|
||||
|
||||
if simple {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(":"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if emitter.canonical || emitter.column > emitter.best_width {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(":"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a block item node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
|
||||
if first {
|
||||
if !yaml_emitter_increase_indent(emitter, false,
|
||||
(emitter.mapping_context && !emitter.indention)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if event.event_type == yaml_SEQUENCE_END_EVENT {
|
||||
|
||||
emitter.indent = emitter.indents[len(emitter.indents)-1]
|
||||
emitter.indents = emitter.indents[:len(emitter.indents)-1]
|
||||
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("-"), true, false, true) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
|
||||
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a block key node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
|
||||
if first {
|
||||
if !yaml_emitter_increase_indent(emitter, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if event.event_type == yaml_MAPPING_END_EVENT {
|
||||
emitter.indent = emitter.indents[len(emitter.indents)-1]
|
||||
emitter.indents = emitter.indents[:len(emitter.indents)-1]
|
||||
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
if yaml_emitter_check_simple_key(emitter) {
|
||||
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
|
||||
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, true)
|
||||
} else {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("?"), true, false, true) {
|
||||
return false
|
||||
}
|
||||
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE)
|
||||
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a block value node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t,
|
||||
event *yaml_event_t, simple bool) bool {
|
||||
|
||||
if simple {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(":"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(":"), true, false, true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
|
||||
|
||||
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a node.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
|
||||
root bool, sequence bool, mapping bool, simple_key bool) bool {
|
||||
emitter.root_context = root
|
||||
emitter.sequence_context = sequence
|
||||
emitter.mapping_context = mapping
|
||||
emitter.simple_key_context = simple_key
|
||||
|
||||
switch event.event_type {
|
||||
case yaml_ALIAS_EVENT:
|
||||
return yaml_emitter_emit_alias(emitter, event)
|
||||
|
||||
case yaml_SCALAR_EVENT:
|
||||
return yaml_emitter_emit_scalar(emitter, event)
|
||||
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
return yaml_emitter_emit_sequence_start(emitter, event)
|
||||
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
return yaml_emitter_emit_mapping_start(emitter, event)
|
||||
|
||||
default:
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect ALIAS.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
if !yaml_emitter_process_anchor(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect SCALAR.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
if !yaml_emitter_select_scalar_style(emitter, event) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_process_anchor(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_process_tag(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_increase_indent(emitter, true, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_process_scalar(emitter) {
|
||||
return false
|
||||
}
|
||||
emitter.indent = emitter.indents[len(emitter.indents)-1]
|
||||
emitter.indents = emitter.indents[:len(emitter.indents)-1]
|
||||
|
||||
emitter.state = emitter.states[len(emitter.states)-1]
|
||||
emitter.states = emitter.states[:len(emitter.states)-1]
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect SEQUENCE-START.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
if !yaml_emitter_process_anchor(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_process_tag(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
if emitter.flow_level > 0 || emitter.canonical ||
|
||||
event.style == yaml_style_t(yaml_FLOW_SEQUENCE_STYLE) ||
|
||||
yaml_emitter_check_empty_sequence(emitter) {
|
||||
emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE
|
||||
} else {
|
||||
emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect MAPPING-START.
|
||||
*/
|
||||
|
||||
func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
if !yaml_emitter_process_anchor(emitter) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_process_tag(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
if emitter.flow_level > 0 || emitter.canonical ||
|
||||
event.style == yaml_style_t(yaml_FLOW_MAPPING_STYLE) ||
|
||||
yaml_emitter_check_empty_mapping(emitter) {
|
||||
emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE
|
||||
} else {
|
||||
emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the document content is an empty scalar.
|
||||
*/
|
||||
|
||||
func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the next events represent an empty sequence.
|
||||
*/
|
||||
|
||||
func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool {
|
||||
if len(emitter.events)-emitter.events_head < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return (emitter.events[emitter.events_head].event_type == yaml_SEQUENCE_START_EVENT &&
|
||||
emitter.events[emitter.events_head+1].event_type == yaml_SEQUENCE_END_EVENT)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the next events represent an empty mapping.
|
||||
*/
|
||||
|
||||
func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool {
|
||||
if len(emitter.events)-emitter.events_head < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
return (emitter.events[emitter.events_head].event_type == yaml_MAPPING_START_EVENT &&
|
||||
emitter.events[emitter.events_head+1].event_type == yaml_MAPPING_END_EVENT)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the next node can be expressed as a simple key.
|
||||
*/
|
||||
|
||||
func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool {
|
||||
length := 0
|
||||
|
||||
switch emitter.events[emitter.events_head].event_type {
|
||||
case yaml_ALIAS_EVENT:
|
||||
length += len(emitter.anchor_data.anchor)
|
||||
|
||||
case yaml_SCALAR_EVENT:
|
||||
if emitter.scalar_data.multiline {
|
||||
return false
|
||||
}
|
||||
length += len(emitter.anchor_data.anchor) +
|
||||
len(emitter.tag_data.handle) +
|
||||
len(emitter.tag_data.suffix) +
|
||||
len(emitter.scalar_data.value)
|
||||
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
if !yaml_emitter_check_empty_sequence(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
length += len(emitter.anchor_data.anchor) +
|
||||
len(emitter.tag_data.handle) +
|
||||
len(emitter.tag_data.suffix)
|
||||
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
if !yaml_emitter_check_empty_mapping(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
length += len(emitter.anchor_data.anchor) +
|
||||
len(emitter.tag_data.handle) +
|
||||
len(emitter.tag_data.suffix)
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if length > 128 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine an acceptable scalar style.
|
||||
*/
|
||||
|
||||
func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0
|
||||
|
||||
if no_tag && !event.implicit && !event.quoted_implicit {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"neither tag nor implicit flags are specified")
|
||||
}
|
||||
|
||||
style := yaml_scalar_style_t(event.style)
|
||||
|
||||
if style == yaml_ANY_SCALAR_STYLE {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
|
||||
if emitter.canonical {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
|
||||
if emitter.simple_key_context && emitter.scalar_data.multiline {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
|
||||
if style == yaml_PLAIN_SCALAR_STYLE {
|
||||
if (emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed) ||
|
||||
(emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed) {
|
||||
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
if len(emitter.scalar_data.value) == 0 &&
|
||||
(emitter.flow_level > 0 || emitter.simple_key_context) {
|
||||
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
if no_tag && !event.implicit {
|
||||
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
}
|
||||
|
||||
if style == yaml_SINGLE_QUOTED_SCALAR_STYLE {
|
||||
if !emitter.scalar_data.single_quoted_allowed {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
}
|
||||
|
||||
if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE {
|
||||
if !emitter.scalar_data.block_allowed ||
|
||||
emitter.flow_level > 0 || emitter.simple_key_context {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
}
|
||||
}
|
||||
|
||||
if no_tag && !event.quoted_implicit &&
|
||||
style != yaml_PLAIN_SCALAR_STYLE {
|
||||
emitter.tag_data.handle = []byte("!")
|
||||
}
|
||||
|
||||
emitter.scalar_data.style = style
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Write an achor.
|
||||
*/
|
||||
|
||||
func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool {
|
||||
if emitter.anchor_data.anchor == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
indicator := "*"
|
||||
if !emitter.anchor_data.alias {
|
||||
indicator = "&"
|
||||
}
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(indicator), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor)
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a tag.
|
||||
*/
|
||||
|
||||
func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool {
|
||||
if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(emitter.tag_data.handle) > 0 {
|
||||
if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(emitter.tag_data.suffix) > 0 {
|
||||
if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(">"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a scalar.
|
||||
*/
|
||||
|
||||
func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
|
||||
switch emitter.scalar_data.style {
|
||||
case yaml_PLAIN_SCALAR_STYLE:
|
||||
return yaml_emitter_write_plain_scalar(emitter,
|
||||
emitter.scalar_data.value,
|
||||
!emitter.simple_key_context)
|
||||
|
||||
case yaml_SINGLE_QUOTED_SCALAR_STYLE:
|
||||
return yaml_emitter_write_single_quoted_scalar(emitter,
|
||||
emitter.scalar_data.value,
|
||||
!emitter.simple_key_context)
|
||||
|
||||
case yaml_DOUBLE_QUOTED_SCALAR_STYLE:
|
||||
return yaml_emitter_write_double_quoted_scalar(emitter,
|
||||
emitter.scalar_data.value,
|
||||
!emitter.simple_key_context)
|
||||
|
||||
case yaml_LITERAL_SCALAR_STYLE:
|
||||
return yaml_emitter_write_literal_scalar(emitter,
|
||||
emitter.scalar_data.value)
|
||||
|
||||
case yaml_FOLDED_SCALAR_STYLE:
|
||||
return yaml_emitter_write_folded_scalar(emitter,
|
||||
emitter.scalar_data.value)
|
||||
|
||||
default:
|
||||
panic("unknown scalar")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a %YAML directive is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t,
|
||||
version_directive yaml_version_directive_t) bool {
|
||||
if version_directive.major != 1 || version_directive.minor != 1 {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"incompatible %YAML directive")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a %TAG directive is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t,
|
||||
tag_directive *yaml_tag_directive_t) bool {
|
||||
handle := tag_directive.handle
|
||||
prefix := tag_directive.prefix
|
||||
|
||||
if len(handle) == 0 {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag handle must not be empty")
|
||||
}
|
||||
|
||||
if handle[0] != '!' {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag handle must start with '!'")
|
||||
}
|
||||
|
||||
if handle[len(handle)-1] != '!' {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag handle must end with '!'")
|
||||
}
|
||||
|
||||
for i := 1; i < len(handle)-1; width(handle[i]) {
|
||||
if !is_alpha(handle[i]) {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag handle must contain alphanumerical characters only")
|
||||
}
|
||||
}
|
||||
|
||||
if len(prefix) == 0 {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag prefix must not be empty")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an anchor is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t,
|
||||
anchor []byte, alias bool) bool {
|
||||
if len(anchor) == 0 {
|
||||
errmsg := "alias value must not be empty"
|
||||
if !alias {
|
||||
errmsg = "anchor value must not be empty"
|
||||
}
|
||||
return yaml_emitter_set_emitter_error(emitter, errmsg)
|
||||
}
|
||||
|
||||
for i := 0; i < len(anchor); i += width(anchor[i]) {
|
||||
if !is_alpha(anchor[i]) {
|
||||
errmsg := "alias value must contain alphanumerical characters only"
|
||||
if !alias {
|
||||
errmsg = "anchor value must contain alphanumerical characters only"
|
||||
}
|
||||
return yaml_emitter_set_emitter_error(emitter, errmsg)
|
||||
}
|
||||
}
|
||||
|
||||
emitter.anchor_data.anchor = anchor
|
||||
emitter.anchor_data.alias = alias
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a tag is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
|
||||
if len(tag) == 0 {
|
||||
return yaml_emitter_set_emitter_error(emitter,
|
||||
"tag value must not be empty")
|
||||
}
|
||||
|
||||
for i := range emitter.tag_directives {
|
||||
tag_directive := &emitter.tag_directives[i]
|
||||
if bytes.HasPrefix(tag, tag_directive.prefix) {
|
||||
emitter.tag_data.handle = tag_directive.handle
|
||||
emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
emitter.tag_data.suffix = tag
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a scalar is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
|
||||
block_indicators := false
|
||||
flow_indicators := false
|
||||
line_breaks := false
|
||||
special_characters := false
|
||||
|
||||
leading_space := false
|
||||
leading_break := false
|
||||
trailing_space := false
|
||||
trailing_break := false
|
||||
break_space := false
|
||||
space_break := false
|
||||
|
||||
preceeded_by_whitespace := false
|
||||
followed_by_whitespace := false
|
||||
previous_space := false
|
||||
previous_break := false
|
||||
|
||||
emitter.scalar_data.value = value
|
||||
|
||||
if len(value) == 0 {
|
||||
emitter.scalar_data.multiline = false
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
emitter.scalar_data.block_plain_allowed = true
|
||||
emitter.scalar_data.single_quoted_allowed = true
|
||||
emitter.scalar_data.block_allowed = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') ||
|
||||
(value[0] == '.' && value[1] == '.' && value[2] == '.')) {
|
||||
block_indicators = true
|
||||
flow_indicators = true
|
||||
}
|
||||
|
||||
preceeded_by_whitespace = true
|
||||
|
||||
for i, w := 0, 0; i < len(value); i += w {
|
||||
w = width(value[i])
|
||||
followed_by_whitespace = i+w >= len(value) || is_blankz_at(value, i+w)
|
||||
|
||||
if i == 0 {
|
||||
switch value[i] {
|
||||
case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`':
|
||||
flow_indicators = true
|
||||
block_indicators = true
|
||||
case '?', ':':
|
||||
flow_indicators = true
|
||||
if followed_by_whitespace {
|
||||
block_indicators = true
|
||||
}
|
||||
case '-':
|
||||
if followed_by_whitespace {
|
||||
flow_indicators = true
|
||||
block_indicators = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch value[i] {
|
||||
case ',', '?', '[', ']', '{', '}':
|
||||
flow_indicators = true
|
||||
case ':':
|
||||
flow_indicators = true
|
||||
if followed_by_whitespace {
|
||||
block_indicators = true
|
||||
}
|
||||
case '#':
|
||||
if preceeded_by_whitespace {
|
||||
flow_indicators = true
|
||||
block_indicators = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !is_printable_at(value, i) || (!is_ascii(value[i]) && !emitter.unicode) {
|
||||
special_characters = true
|
||||
}
|
||||
|
||||
if is_break_at(value, i) {
|
||||
line_breaks = true
|
||||
}
|
||||
|
||||
if is_space(value[i]) {
|
||||
if i == 0 {
|
||||
leading_space = true
|
||||
}
|
||||
if i+w == len(value) {
|
||||
trailing_space = true
|
||||
}
|
||||
if previous_break {
|
||||
break_space = true
|
||||
}
|
||||
previous_space = true
|
||||
previous_break = false
|
||||
} else if is_break_at(value, i) {
|
||||
if i == 0 {
|
||||
leading_break = true
|
||||
}
|
||||
if i+width(value[i]) == len(value) {
|
||||
trailing_break = true
|
||||
}
|
||||
if previous_space {
|
||||
space_break = true
|
||||
}
|
||||
previous_space = false
|
||||
previous_break = true
|
||||
} else {
|
||||
previous_space = false
|
||||
previous_break = false
|
||||
}
|
||||
|
||||
preceeded_by_whitespace = is_blankz_at(value, i)
|
||||
}
|
||||
|
||||
emitter.scalar_data.multiline = line_breaks
|
||||
|
||||
emitter.scalar_data.flow_plain_allowed = true
|
||||
emitter.scalar_data.block_plain_allowed = true
|
||||
emitter.scalar_data.single_quoted_allowed = true
|
||||
emitter.scalar_data.block_allowed = true
|
||||
|
||||
if leading_space || leading_break || trailing_space || trailing_break {
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
emitter.scalar_data.block_plain_allowed = false
|
||||
}
|
||||
|
||||
if trailing_space {
|
||||
emitter.scalar_data.block_allowed = false
|
||||
}
|
||||
|
||||
if break_space {
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
emitter.scalar_data.block_plain_allowed = false
|
||||
emitter.scalar_data.single_quoted_allowed = false
|
||||
}
|
||||
|
||||
if space_break || special_characters {
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
emitter.scalar_data.block_plain_allowed = false
|
||||
emitter.scalar_data.single_quoted_allowed = false
|
||||
emitter.scalar_data.block_allowed = false
|
||||
}
|
||||
|
||||
if line_breaks {
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
emitter.scalar_data.block_plain_allowed = false
|
||||
}
|
||||
|
||||
if flow_indicators {
|
||||
emitter.scalar_data.flow_plain_allowed = false
|
||||
}
|
||||
|
||||
if block_indicators {
|
||||
emitter.scalar_data.block_plain_allowed = false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the event data is valid.
|
||||
*/
|
||||
|
||||
func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
|
||||
emitter.anchor_data.anchor = nil
|
||||
emitter.tag_data.handle = nil
|
||||
emitter.tag_data.suffix = nil
|
||||
emitter.scalar_data.value = nil
|
||||
|
||||
switch event.event_type {
|
||||
case yaml_ALIAS_EVENT:
|
||||
if !yaml_emitter_analyze_anchor(emitter,
|
||||
event.anchor, true) {
|
||||
return false
|
||||
}
|
||||
|
||||
case yaml_SCALAR_EVENT:
|
||||
if len(event.anchor) > 0 {
|
||||
if !yaml_emitter_analyze_anchor(emitter,
|
||||
event.anchor, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(event.tag) > 0 && (emitter.canonical ||
|
||||
(!event.implicit &&
|
||||
!event.quoted_implicit)) {
|
||||
if !yaml_emitter_analyze_tag(emitter, event.tag) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !yaml_emitter_analyze_scalar(emitter, event.value) {
|
||||
return false
|
||||
}
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
if len(event.anchor) > 0 {
|
||||
if !yaml_emitter_analyze_anchor(emitter,
|
||||
event.anchor, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(event.tag) > 0 && (emitter.canonical ||
|
||||
!event.implicit) {
|
||||
if !yaml_emitter_analyze_tag(emitter,
|
||||
event.tag) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
if len(event.anchor) > 0 {
|
||||
if !yaml_emitter_analyze_anchor(emitter,
|
||||
event.anchor, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(event.tag) > 0 && (emitter.canonical ||
|
||||
!event.implicit) {
|
||||
if !yaml_emitter_analyze_tag(emitter,
|
||||
event.tag) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the BOM character.
|
||||
*/
|
||||
|
||||
func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool {
|
||||
if !flush(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
pos := emitter.buffer_pos
|
||||
emitter.buffer[pos] = '\xEF'
|
||||
emitter.buffer[pos+1] = '\xBB'
|
||||
emitter.buffer[pos+2] = '\xBF'
|
||||
emitter.buffer_pos += 3
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
|
||||
indent := emitter.indent
|
||||
if indent < 0 {
|
||||
indent = 0
|
||||
}
|
||||
|
||||
if !emitter.indention || emitter.column > indent ||
|
||||
(emitter.column == indent && !emitter.whitespace) {
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for emitter.column < indent {
|
||||
if !put(emitter, ' ') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = true
|
||||
emitter.indention = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_indicator(emitter *yaml_emitter_t,
|
||||
indicator []byte, need_whitespace bool,
|
||||
is_whitespace bool, is_indention bool) bool {
|
||||
if need_whitespace && !emitter.whitespace {
|
||||
if !put(emitter, ' ') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
ind_pos := 0
|
||||
for ind_pos < len(indicator) {
|
||||
if !write(emitter, indicator, &ind_pos) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = is_whitespace
|
||||
emitter.indention = (emitter.indention && is_indention)
|
||||
emitter.open_ended = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool {
|
||||
pos := 0
|
||||
for pos < len(value) {
|
||||
if !write(emitter, value, &pos) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool {
|
||||
if !emitter.whitespace {
|
||||
if !put(emitter, ' ') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
pos := 0
|
||||
for pos < len(value) {
|
||||
if !write(emitter, value, &pos) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte,
|
||||
need_whitespace bool) bool {
|
||||
if need_whitespace && !emitter.whitespace {
|
||||
if !put(emitter, ' ') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
write_it := false
|
||||
switch value[i] {
|
||||
case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_',
|
||||
'.', '!', '~', '*', '\'', '(', ')', '[', ']':
|
||||
write_it = true
|
||||
default:
|
||||
write_it = is_alpha(value[i])
|
||||
}
|
||||
if write_it {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
w := width(value[i])
|
||||
for j := 0; j < w; j++ {
|
||||
val := value[i]
|
||||
i++
|
||||
|
||||
if !put(emitter, '%') {
|
||||
return false
|
||||
}
|
||||
c := val >> 4
|
||||
if c < 10 {
|
||||
c += '0'
|
||||
} else {
|
||||
c += 'A' - 10
|
||||
}
|
||||
if !put(emitter, c) {
|
||||
return false
|
||||
}
|
||||
|
||||
c = val & 0x0f
|
||||
if c < 10 {
|
||||
c += '0'
|
||||
} else {
|
||||
c += 'A' - 10
|
||||
}
|
||||
if !put(emitter, c) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte,
|
||||
allow_breaks bool) bool {
|
||||
spaces := false
|
||||
breaks := false
|
||||
|
||||
if !emitter.whitespace {
|
||||
if !put(emitter, ' ') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
if is_space(value[i]) {
|
||||
if allow_breaks && !spaces &&
|
||||
emitter.column > emitter.best_width &&
|
||||
!is_space(value[i+1]) {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
i += width(value[i])
|
||||
} else {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
spaces = true
|
||||
} else if is_break_at(value, i) {
|
||||
if !breaks && value[i] == '\n' {
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !write_break(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = true
|
||||
breaks = true
|
||||
} else {
|
||||
if breaks {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = false
|
||||
spaces = false
|
||||
breaks = false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
if emitter.root_context {
|
||||
emitter.open_ended = true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte,
|
||||
allow_breaks bool) bool {
|
||||
spaces := false
|
||||
breaks := false
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("'"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
if is_space(value[i]) {
|
||||
if allow_breaks && !spaces &&
|
||||
emitter.column > emitter.best_width &&
|
||||
i > 0 && i < len(value)-1 &&
|
||||
!is_space(value[i+1]) {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
i += width(value[i])
|
||||
} else {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
spaces = true
|
||||
} else if is_break_at(value, i) {
|
||||
if !breaks && value[i] == '\n' {
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !write_break(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = true
|
||||
breaks = true
|
||||
} else {
|
||||
if breaks {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if value[i] == '\'' {
|
||||
if !put(emitter, '\'') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = false
|
||||
spaces = false
|
||||
breaks = false
|
||||
}
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("'"), false, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte,
|
||||
allow_breaks bool) bool {
|
||||
|
||||
spaces := false
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("\""), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
if !is_printable_at(value, i) || (!emitter.unicode && !is_ascii(value[i])) ||
|
||||
is_bom_at(value, i) || is_break_at(value, i) ||
|
||||
value[i] == '"' || value[i] == '\\' {
|
||||
octet := value[i]
|
||||
|
||||
var w int
|
||||
var v rune
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
w, v = 1, rune(octet&0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
w, v = 2, rune(octet&0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
w, v = 3, rune(octet&0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
w, v = 4, rune(octet&0x07)
|
||||
}
|
||||
|
||||
for k := 1; k < w; k++ {
|
||||
octet = value[i+k]
|
||||
v = (v << 6) + (rune(octet) & 0x3F)
|
||||
}
|
||||
i += w
|
||||
|
||||
if !put(emitter, '\\') {
|
||||
return false
|
||||
}
|
||||
|
||||
switch v {
|
||||
case 0x00:
|
||||
if !put(emitter, '0') {
|
||||
return false
|
||||
}
|
||||
case 0x07:
|
||||
if !put(emitter, 'a') {
|
||||
return false
|
||||
}
|
||||
case 0x08:
|
||||
if !put(emitter, 'b') {
|
||||
return false
|
||||
}
|
||||
case 0x09:
|
||||
if !put(emitter, 't') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x0A:
|
||||
if !put(emitter, 'n') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x0B:
|
||||
if !put(emitter, 'v') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x0C:
|
||||
if !put(emitter, 'f') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x0D:
|
||||
if !put(emitter, 'r') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x1B:
|
||||
if !put(emitter, 'e') {
|
||||
return false
|
||||
}
|
||||
case 0x22:
|
||||
if !put(emitter, '"') {
|
||||
return false
|
||||
}
|
||||
case 0x5C:
|
||||
if !put(emitter, '\\') {
|
||||
return false
|
||||
}
|
||||
case 0x85:
|
||||
if !put(emitter, 'N') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0xA0:
|
||||
if !put(emitter, '_') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x2028:
|
||||
if !put(emitter, 'L') {
|
||||
return false
|
||||
}
|
||||
|
||||
case 0x2029:
|
||||
if !put(emitter, 'P') {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if v <= 0xFF {
|
||||
if !put(emitter, 'x') {
|
||||
return false
|
||||
}
|
||||
w = 2
|
||||
} else if v <= 0xFFFF {
|
||||
if !put(emitter, 'u') {
|
||||
return false
|
||||
}
|
||||
w = 4
|
||||
} else {
|
||||
if !put(emitter, 'U') {
|
||||
return false
|
||||
}
|
||||
w = 8
|
||||
}
|
||||
for k := (w - 1) * 4; k >= 0; k -= 4 {
|
||||
digit := byte((v >> uint(k)) & 0x0F)
|
||||
c := digit + '0'
|
||||
if c > 9 {
|
||||
c = digit + 'A' - 10
|
||||
}
|
||||
if !put(emitter, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
spaces = false
|
||||
} else if is_space(value[i]) {
|
||||
if allow_breaks && !spaces &&
|
||||
emitter.column > emitter.best_width &&
|
||||
i > 0 && i < len(value)-1 {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
if is_space(value[i+1]) {
|
||||
if !put(emitter, '\\') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
i += width(value[i])
|
||||
} else {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
spaces = true
|
||||
} else {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
spaces = false
|
||||
}
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("\""), false, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.whitespace = false
|
||||
emitter.indention = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool {
|
||||
|
||||
if is_space(value[0]) || is_break_at(value, 0) {
|
||||
indent_hint := []byte{'0' + byte(emitter.best_indent)}
|
||||
if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
emitter.open_ended = false
|
||||
|
||||
var chomp_hint [1]byte
|
||||
if len(value) == 0 {
|
||||
chomp_hint[0] = '-'
|
||||
} else {
|
||||
i := len(value) - 1
|
||||
for value[i]&0xC0 == 0x80 {
|
||||
i--
|
||||
}
|
||||
|
||||
if !is_break_at(value, i) {
|
||||
chomp_hint[0] = '-'
|
||||
} else if i == 0 {
|
||||
chomp_hint[0] = '+'
|
||||
emitter.open_ended = true
|
||||
} else {
|
||||
for value[i]&0xC0 == 0x80 {
|
||||
i--
|
||||
}
|
||||
|
||||
if is_break_at(value, i) {
|
||||
chomp_hint[0] = '+'
|
||||
emitter.open_ended = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if chomp_hint[0] != 0 {
|
||||
if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool {
|
||||
|
||||
breaks := true
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte("|"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
|
||||
emitter.indention = true
|
||||
emitter.whitespace = true
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
if is_break_at(value, i) {
|
||||
if !write_break(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = true
|
||||
breaks = true
|
||||
} else {
|
||||
if breaks {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = false
|
||||
breaks = false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool {
|
||||
breaks := true
|
||||
leading_spaces := true
|
||||
|
||||
if !yaml_emitter_write_indicator(emitter, []byte(">"), true, false, false) {
|
||||
return false
|
||||
}
|
||||
if !yaml_emitter_write_block_scalar_hints(emitter, value) {
|
||||
return false
|
||||
}
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = true
|
||||
emitter.whitespace = true
|
||||
|
||||
for i := 0; i < len(value); {
|
||||
if is_break_at(value, i) {
|
||||
if !breaks && !leading_spaces && value[i] == '\n' {
|
||||
k := i
|
||||
for is_break_at(value, k) {
|
||||
k += width(value[k])
|
||||
}
|
||||
if !is_blankz_at(value, k) {
|
||||
if !put_break(emitter) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !write_break(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
emitter.indention = true
|
||||
breaks = true
|
||||
} else {
|
||||
if breaks {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
leading_spaces = is_blank(value[i])
|
||||
}
|
||||
if !breaks && is_space(value[i]) && !is_space(value[i+1]) &&
|
||||
emitter.column > emitter.best_width {
|
||||
if !yaml_emitter_write_indent(emitter) {
|
||||
return false
|
||||
}
|
||||
i += width(value[i])
|
||||
} else {
|
||||
if !write(emitter, value, &i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
emitter.indention = false
|
||||
breaks = false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timeTimeType = reflect.TypeOf(time.Time{})
|
||||
marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
||||
numberType = reflect.TypeOf(Number(""))
|
||||
nonPrintable = regexp.MustCompile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD]")
|
||||
multiline = regexp.MustCompile("\n|\u0085|\u2028|\u2029")
|
||||
|
||||
shortTags = map[string]string{
|
||||
yaml_NULL_TAG: "!!null",
|
||||
yaml_BOOL_TAG: "!!bool",
|
||||
yaml_STR_TAG: "!!str",
|
||||
yaml_INT_TAG: "!!int",
|
||||
yaml_FLOAT_TAG: "!!float",
|
||||
yaml_TIMESTAMP_TAG: "!!timestamp",
|
||||
yaml_SEQ_TAG: "!!seq",
|
||||
yaml_MAP_TAG: "!!map",
|
||||
yaml_BINARY_TAG: "!!binary",
|
||||
}
|
||||
)
|
||||
|
||||
type Marshaler interface {
|
||||
MarshalYAML() (tag string, value interface{}, err error)
|
||||
}
|
||||
|
||||
// An Encoder writes JSON objects to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
emitter yaml_emitter_t
|
||||
event yaml_event_t
|
||||
flow bool
|
||||
err error
|
||||
}
|
||||
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
b := bytes.Buffer{}
|
||||
e := NewEncoder(&b)
|
||||
err := e.Encode(v)
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
e := &Encoder{w: w}
|
||||
yaml_emitter_initialize(&e.emitter)
|
||||
yaml_emitter_set_output_writer(&e.emitter, e.w)
|
||||
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
|
||||
e.emit()
|
||||
yaml_document_start_event_initialize(&e.event, nil, nil, true)
|
||||
e.emit()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Encoder) Encode(v interface{}) (err error) {
|
||||
defer recovery(&err)
|
||||
|
||||
if e.err != nil {
|
||||
return e.err
|
||||
}
|
||||
|
||||
e.marshal("", reflect.ValueOf(v), true)
|
||||
|
||||
yaml_document_end_event_initialize(&e.event, true)
|
||||
e.emit()
|
||||
e.emitter.open_ended = false
|
||||
yaml_stream_end_event_initialize(&e.event)
|
||||
e.emit()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Encoder) emit() {
|
||||
if !yaml_emitter_emit(&e.emitter, &e.event) {
|
||||
panic("bad emit")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) marshal(tag string, v reflect.Value, allowAddr bool) {
|
||||
vt := v.Type()
|
||||
|
||||
if vt.Implements(marshalerType) {
|
||||
e.emitMarshaler(tag, v)
|
||||
return
|
||||
}
|
||||
|
||||
if vt.Kind() != reflect.Ptr && allowAddr {
|
||||
if reflect.PtrTo(vt).Implements(marshalerType) {
|
||||
e.emitAddrMarshaler(tag, v)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Interface:
|
||||
if v.IsNil() {
|
||||
e.emitNil()
|
||||
} else {
|
||||
e.marshal(tag, v.Elem(), allowAddr)
|
||||
}
|
||||
case reflect.Map:
|
||||
e.emitMap(tag, v)
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
e.emitNil()
|
||||
} else {
|
||||
e.marshal(tag, v.Elem(), true)
|
||||
}
|
||||
case reflect.Struct:
|
||||
e.emitStruct(tag, v)
|
||||
case reflect.Slice:
|
||||
e.emitSlice(tag, v)
|
||||
case reflect.String:
|
||||
e.emitString(tag, v)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
e.emitInt(tag, v)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
e.emitUint(tag, v)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
e.emitFloat(tag, v)
|
||||
case reflect.Bool:
|
||||
e.emitBool(tag, v)
|
||||
default:
|
||||
panic("Can't marshal type yet: " + v.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Encoder) emitMap(tag string, v reflect.Value) {
|
||||
e.mapping(tag, func() {
|
||||
var keys stringValues = v.MapKeys()
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
e.marshal("", k, true)
|
||||
e.marshal("", v.MapIndex(k), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *Encoder) emitStruct(tag string, v reflect.Value) {
|
||||
if v.Type() == timeTimeType {
|
||||
e.emitTime(tag, v)
|
||||
return
|
||||
}
|
||||
|
||||
fields := cachedTypeFields(v.Type())
|
||||
|
||||
e.mapping(tag, func() {
|
||||
for _, f := range fields {
|
||||
fv := fieldByIndex(v, f.index)
|
||||
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
|
||||
continue
|
||||
}
|
||||
|
||||
e.marshal("", reflect.ValueOf(f.name), true)
|
||||
e.flow = f.flow
|
||||
e.marshal("", fv, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *Encoder) emitTime(tag string, v reflect.Value) {
|
||||
t := v.Interface().(time.Time)
|
||||
bytes, _ := t.MarshalText()
|
||||
e.emitScalar(string(bytes), "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *Encoder) mapping(tag string, f func()) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_MAPPING_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_MAPPING_STYLE
|
||||
}
|
||||
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
|
||||
e.emit()
|
||||
|
||||
f()
|
||||
|
||||
yaml_mapping_end_event_initialize(&e.event)
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *Encoder) emitSlice(tag string, v reflect.Value) {
|
||||
if v.Type() == byteSliceType {
|
||||
e.emitBase64(tag, v)
|
||||
return
|
||||
}
|
||||
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_SEQUENCE_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_SEQUENCE_STYLE
|
||||
}
|
||||
yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
|
||||
e.emit()
|
||||
|
||||
n := v.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
e.marshal("", v.Index(i), true)
|
||||
}
|
||||
|
||||
yaml_sequence_end_event_initialize(&e.event)
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *Encoder) emitBase64(tag string, v reflect.Value) {
|
||||
if v.IsNil() {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
|
||||
s := v.Bytes()
|
||||
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
|
||||
|
||||
base64.StdEncoding.Encode(dst, s)
|
||||
e.emitScalar(string(dst), "", yaml_BINARY_TAG, yaml_DOUBLE_QUOTED_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitString(tag string, v reflect.Value) {
|
||||
var style yaml_scalar_style_t
|
||||
s := v.String()
|
||||
|
||||
if nonPrintable.MatchString(s) {
|
||||
e.emitBase64(tag, v)
|
||||
return
|
||||
}
|
||||
|
||||
if v.Type() == numberType {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
} else {
|
||||
event := yaml_event_t{
|
||||
implicit: true,
|
||||
value: []byte(s),
|
||||
}
|
||||
|
||||
rtag, _ := resolveInterface(event, false)
|
||||
if tag == "" && rtag != yaml_STR_TAG {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
} else if multiline.MatchString(s) {
|
||||
style = yaml_LITERAL_SCALAR_STYLE
|
||||
} else {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
}
|
||||
|
||||
e.emitScalar(s, "", tag, style)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitBool(tag string, v reflect.Value) {
|
||||
s := strconv.FormatBool(v.Bool())
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitInt(tag string, v reflect.Value) {
|
||||
s := strconv.FormatInt(v.Int(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitUint(tag string, v reflect.Value) {
|
||||
s := strconv.FormatUint(v.Uint(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitFloat(tag string, v reflect.Value) {
|
||||
f := v.Float()
|
||||
|
||||
var s string
|
||||
switch {
|
||||
case math.IsNaN(f):
|
||||
s = ".nan"
|
||||
case math.IsInf(f, 1):
|
||||
s = "+.inf"
|
||||
case math.IsInf(f, -1):
|
||||
s = "-.inf"
|
||||
default:
|
||||
s = strconv.FormatFloat(f, 'g', -1, v.Type().Bits())
|
||||
}
|
||||
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitNil() {
|
||||
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
||||
implicit := tag == ""
|
||||
if !implicit {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
|
||||
stag := shortTags[tag]
|
||||
if stag == "" {
|
||||
stag = tag
|
||||
}
|
||||
|
||||
yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(stag), []byte(value), implicit, implicit, style)
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *Encoder) emitMarshaler(tag string, v reflect.Value) {
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
|
||||
m := v.Interface().(Marshaler)
|
||||
if m == nil {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
t, val, err := m.MarshalYAML()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == nil {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
|
||||
e.marshal(t, reflect.ValueOf(val), false)
|
||||
}
|
||||
|
||||
func (e *Encoder) emitAddrMarshaler(tag string, v reflect.Value) {
|
||||
if !v.CanAddr() {
|
||||
e.marshal(tag, v, false)
|
||||
return
|
||||
}
|
||||
|
||||
va := v.Addr()
|
||||
if va.IsNil() {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
|
||||
m := v.Interface().(Marshaler)
|
||||
t, val, err := m.MarshalYAML()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
e.emitNil()
|
||||
return
|
||||
}
|
||||
|
||||
e.marshal(t, reflect.ValueOf(val), false)
|
||||
}
|
|
@ -1,1230 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
/*
|
||||
* The parser implements the following grammar:
|
||||
*
|
||||
* stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
||||
* implicit_document ::= block_node DOCUMENT-END*
|
||||
* explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||
* block_node_or_indentless_sequence ::=
|
||||
* ALIAS
|
||||
* | properties (block_content | indentless_block_sequence)?
|
||||
* | block_content
|
||||
* | indentless_block_sequence
|
||||
* block_node ::= ALIAS
|
||||
* | properties block_content?
|
||||
* | block_content
|
||||
* flow_node ::= ALIAS
|
||||
* | properties flow_content?
|
||||
* | flow_content
|
||||
* properties ::= TAG ANCHOR? | ANCHOR TAG?
|
||||
* block_content ::= block_collection | flow_collection | SCALAR
|
||||
* flow_content ::= flow_collection | SCALAR
|
||||
* block_collection ::= block_sequence | block_mapping
|
||||
* flow_collection ::= flow_sequence | flow_mapping
|
||||
* block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
||||
* indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
||||
* block_mapping ::= BLOCK-MAPPING_START
|
||||
* ((KEY block_node_or_indentless_sequence?)?
|
||||
* (VALUE block_node_or_indentless_sequence?)?)*
|
||||
* BLOCK-END
|
||||
* flow_sequence ::= FLOW-SEQUENCE-START
|
||||
* (flow_sequence_entry FLOW-ENTRY)*
|
||||
* flow_sequence_entry?
|
||||
* FLOW-SEQUENCE-END
|
||||
* flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* flow_mapping ::= FLOW-MAPPING-START
|
||||
* (flow_mapping_entry FLOW-ENTRY)*
|
||||
* flow_mapping_entry?
|
||||
* FLOW-MAPPING-END
|
||||
* flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
*/
|
||||
|
||||
/*
|
||||
* Peek the next token in the token queue.
|
||||
*/
|
||||
func peek_token(parser *yaml_parser_t) *yaml_token_t {
|
||||
if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
|
||||
return &parser.tokens[parser.tokens_head]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the next token from the queue (must be called after peek_token).
|
||||
*/
|
||||
func skip_token(parser *yaml_parser_t) {
|
||||
parser.token_available = false
|
||||
parser.tokens_parsed++
|
||||
parser.stream_end_produced = parser.tokens[parser.tokens_head].token_type == yaml_STREAM_END_TOKEN
|
||||
parser.tokens_head++
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next event.
|
||||
*/
|
||||
|
||||
func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||
/* Erase the event object. */
|
||||
*event = yaml_event_t{}
|
||||
|
||||
/* No events after the end of the stream or error. */
|
||||
|
||||
if parser.stream_end_produced || parser.error != yaml_NO_ERROR ||
|
||||
parser.state == yaml_PARSE_END_STATE {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Generate the next event. */
|
||||
|
||||
return yaml_parser_state_machine(parser, event)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set parser error.
|
||||
*/
|
||||
|
||||
func yaml_parser_set_parser_error(parser *yaml_parser_t,
|
||||
problem string, problem_mark YAML_mark_t) bool {
|
||||
parser.error = yaml_PARSER_ERROR
|
||||
parser.problem = problem
|
||||
parser.problem_mark = problem_mark
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func yaml_parser_set_parser_error_context(parser *yaml_parser_t,
|
||||
context string, context_mark YAML_mark_t,
|
||||
problem string, problem_mark YAML_mark_t) bool {
|
||||
parser.error = yaml_PARSER_ERROR
|
||||
parser.context = context
|
||||
parser.context_mark = context_mark
|
||||
parser.problem = problem
|
||||
parser.problem_mark = problem_mark
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* State dispatcher.
|
||||
*/
|
||||
|
||||
func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||
switch parser.state {
|
||||
case yaml_PARSE_STREAM_START_STATE:
|
||||
return yaml_parser_parse_stream_start(parser, event)
|
||||
|
||||
case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
|
||||
return yaml_parser_parse_document_start(parser, event, true)
|
||||
|
||||
case yaml_PARSE_DOCUMENT_START_STATE:
|
||||
return yaml_parser_parse_document_start(parser, event, false)
|
||||
|
||||
case yaml_PARSE_DOCUMENT_CONTENT_STATE:
|
||||
return yaml_parser_parse_document_content(parser, event)
|
||||
|
||||
case yaml_PARSE_DOCUMENT_END_STATE:
|
||||
return yaml_parser_parse_document_end(parser, event)
|
||||
|
||||
case yaml_PARSE_BLOCK_NODE_STATE:
|
||||
return yaml_parser_parse_node(parser, event, true, false)
|
||||
|
||||
case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
|
||||
return yaml_parser_parse_node(parser, event, true, true)
|
||||
|
||||
case yaml_PARSE_FLOW_NODE_STATE:
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return yaml_parser_parse_block_sequence_entry(parser, event, true)
|
||||
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
|
||||
return yaml_parser_parse_block_sequence_entry(parser, event, false)
|
||||
|
||||
case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
|
||||
return yaml_parser_parse_indentless_sequence_entry(parser, event)
|
||||
|
||||
case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
|
||||
return yaml_parser_parse_block_mapping_key(parser, event, true)
|
||||
|
||||
case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
|
||||
return yaml_parser_parse_block_mapping_key(parser, event, false)
|
||||
|
||||
case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
|
||||
return yaml_parser_parse_block_mapping_value(parser, event)
|
||||
|
||||
case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return yaml_parser_parse_flow_sequence_entry(parser, event, true)
|
||||
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
|
||||
return yaml_parser_parse_flow_sequence_entry(parser, event, false)
|
||||
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
|
||||
return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event)
|
||||
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
|
||||
return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event)
|
||||
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
|
||||
return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event)
|
||||
|
||||
case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
|
||||
return yaml_parser_parse_flow_mapping_key(parser, event, true)
|
||||
|
||||
case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
|
||||
return yaml_parser_parse_flow_mapping_key(parser, event, false)
|
||||
|
||||
case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
|
||||
return yaml_parser_parse_flow_mapping_value(parser, event, false)
|
||||
|
||||
case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
|
||||
return yaml_parser_parse_flow_mapping_value(parser, event, true)
|
||||
}
|
||||
|
||||
panic("invalid parser state")
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the production:
|
||||
* stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
|
||||
* ************
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type != yaml_STREAM_START_TOKEN {
|
||||
return yaml_parser_set_parser_error(parser,
|
||||
"did not find expected <stream-start>", token.start_mark)
|
||||
}
|
||||
|
||||
parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_STREAM_START_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
encoding: token.encoding,
|
||||
}
|
||||
skip_token(parser)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* implicit_document ::= block_node DOCUMENT-END*
|
||||
* *
|
||||
* explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||
* *************************
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t,
|
||||
implicit bool) bool {
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
/* Parse extra document end indicators. */
|
||||
|
||||
if !implicit {
|
||||
for token.token_type == yaml_DOCUMENT_END_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse an implicit document. */
|
||||
|
||||
if implicit && token.token_type != yaml_VERSION_DIRECTIVE_TOKEN &&
|
||||
token.token_type != yaml_TAG_DIRECTIVE_TOKEN &&
|
||||
token.token_type != yaml_DOCUMENT_START_TOKEN &&
|
||||
token.token_type != yaml_STREAM_END_TOKEN {
|
||||
if !yaml_parser_process_directives(parser, nil, nil) {
|
||||
return false
|
||||
}
|
||||
|
||||
parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
|
||||
parser.state = yaml_PARSE_BLOCK_NODE_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_DOCUMENT_START_EVENT,
|
||||
implicit: true,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
} else if token.token_type != yaml_STREAM_END_TOKEN {
|
||||
/* Parse an explicit document. */
|
||||
var version_directive *yaml_version_directive_t
|
||||
var tag_directives []yaml_tag_directive_t
|
||||
|
||||
start_mark := token.start_mark
|
||||
if !yaml_parser_process_directives(parser, &version_directive,
|
||||
&tag_directives) {
|
||||
return false
|
||||
}
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_DOCUMENT_START_TOKEN {
|
||||
yaml_parser_set_parser_error(parser,
|
||||
"did not find expected <document start>", token.start_mark)
|
||||
return false
|
||||
}
|
||||
|
||||
parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
|
||||
parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE
|
||||
|
||||
end_mark := token.end_mark
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_DOCUMENT_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
version_directive: version_directive,
|
||||
tag_directives: tag_directives,
|
||||
implicit: false,
|
||||
}
|
||||
skip_token(parser)
|
||||
} else {
|
||||
/* Parse the stream end. */
|
||||
parser.state = yaml_PARSE_END_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_STREAM_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
skip_token(parser)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||
* ***********
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_VERSION_DIRECTIVE_TOKEN ||
|
||||
token.token_type == yaml_TAG_DIRECTIVE_TOKEN ||
|
||||
token.token_type == yaml_DOCUMENT_START_TOKEN ||
|
||||
token.token_type == yaml_DOCUMENT_END_TOKEN ||
|
||||
token.token_type == yaml_STREAM_END_TOKEN {
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
return yaml_parser_process_empty_scalar(parser, event,
|
||||
token.start_mark)
|
||||
} else {
|
||||
return yaml_parser_parse_node(parser, event, true, false)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* implicit_document ::= block_node DOCUMENT-END*
|
||||
* *************
|
||||
* explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
|
||||
* *************
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
|
||||
implicit := true
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
start_mark, end_mark := token.start_mark, token.start_mark
|
||||
|
||||
if token.token_type == yaml_DOCUMENT_END_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
skip_token(parser)
|
||||
implicit = false
|
||||
}
|
||||
|
||||
parser.tag_directives = parser.tag_directives[:0]
|
||||
|
||||
parser.state = yaml_PARSE_DOCUMENT_START_STATE
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_DOCUMENT_END_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
implicit: implicit,
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* block_node_or_indentless_sequence ::=
|
||||
* ALIAS
|
||||
* *****
|
||||
* | properties (block_content | indentless_block_sequence)?
|
||||
* ********** *
|
||||
* | block_content | indentless_block_sequence
|
||||
* *
|
||||
* block_node ::= ALIAS
|
||||
* *****
|
||||
* | properties block_content?
|
||||
* ********** *
|
||||
* | block_content
|
||||
* *
|
||||
* flow_node ::= ALIAS
|
||||
* *****
|
||||
* | properties flow_content?
|
||||
* ********** *
|
||||
* | flow_content
|
||||
* *
|
||||
* properties ::= TAG ANCHOR? | ANCHOR TAG?
|
||||
* *************************
|
||||
* block_content ::= block_collection | flow_collection | SCALAR
|
||||
* ******
|
||||
* flow_content ::= flow_collection | SCALAR
|
||||
* ******
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t,
|
||||
block bool, indentless_sequence bool) bool {
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_ALIAS_TOKEN {
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_ALIAS_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
anchor: token.value,
|
||||
}
|
||||
skip_token(parser)
|
||||
return true
|
||||
} else {
|
||||
start_mark, end_mark := token.start_mark, token.start_mark
|
||||
|
||||
var tag_handle []byte
|
||||
var tag_suffix, anchor []byte
|
||||
var tag_mark YAML_mark_t
|
||||
if token.token_type == yaml_ANCHOR_TOKEN {
|
||||
anchor = token.value
|
||||
start_mark = token.start_mark
|
||||
end_mark = token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type == yaml_TAG_TOKEN {
|
||||
tag_handle = token.value
|
||||
tag_suffix = token.suffix
|
||||
tag_mark = token.start_mark
|
||||
end_mark = token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else if token.token_type == yaml_TAG_TOKEN {
|
||||
tag_handle = token.value
|
||||
tag_suffix = token.suffix
|
||||
start_mark, tag_mark = token.start_mark, token.start_mark
|
||||
end_mark = token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type == yaml_ANCHOR_TOKEN {
|
||||
anchor = token.value
|
||||
end_mark = token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var tag []byte
|
||||
if tag_handle != nil {
|
||||
if len(tag_handle) == 0 {
|
||||
tag = tag_suffix
|
||||
tag_handle = nil
|
||||
tag_suffix = nil
|
||||
} else {
|
||||
for i := range parser.tag_directives {
|
||||
tag_directive := &parser.tag_directives[i]
|
||||
if bytes.Equal(tag_directive.handle, tag_handle) {
|
||||
tag = append([]byte(nil), tag_directive.prefix...)
|
||||
tag = append(tag, tag_suffix...)
|
||||
tag_handle = nil
|
||||
tag_suffix = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(tag) == 0 {
|
||||
yaml_parser_set_parser_error_context(parser,
|
||||
"while parsing a node", start_mark,
|
||||
"found undefined tag handle", tag_mark)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicit := len(tag) == 0
|
||||
if indentless_sequence && token.token_type == yaml_BLOCK_ENTRY_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
if token.token_type == yaml_SCALAR_TOKEN {
|
||||
plain_implicit := false
|
||||
quoted_implicit := false
|
||||
end_mark = token.end_mark
|
||||
if (token.style == yaml_PLAIN_SCALAR_STYLE && len(tag) == 0) ||
|
||||
(len(tag) == 1 && tag[0] == '!') {
|
||||
plain_implicit = true
|
||||
} else if len(tag) == 0 {
|
||||
quoted_implicit = true
|
||||
}
|
||||
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SCALAR_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
value: token.value,
|
||||
implicit: plain_implicit,
|
||||
quoted_implicit: quoted_implicit,
|
||||
style: yaml_style_t(token.style),
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
return true
|
||||
} else if token.token_type == yaml_FLOW_SEQUENCE_START_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
|
||||
}
|
||||
|
||||
return true
|
||||
} else if token.token_type == yaml_FLOW_MAPPING_START_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
|
||||
}
|
||||
|
||||
return true
|
||||
} else if block && token.token_type == yaml_BLOCK_SEQUENCE_START_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
|
||||
}
|
||||
|
||||
return true
|
||||
} else if block && token.token_type == yaml_BLOCK_MAPPING_START_TOKEN {
|
||||
end_mark = token.end_mark
|
||||
parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_START_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
|
||||
}
|
||||
return true
|
||||
} else if len(anchor) > 0 || len(tag) > 0 {
|
||||
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SCALAR_EVENT,
|
||||
start_mark: start_mark,
|
||||
end_mark: end_mark,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
quoted_implicit: false,
|
||||
style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
msg := "while parsing a block node"
|
||||
if !block {
|
||||
msg = "while parsing a flow node"
|
||||
}
|
||||
yaml_parser_set_parser_error_context(parser, msg, start_mark,
|
||||
"did not find expected node content", token.start_mark)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
|
||||
* ******************** *********** * *********
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_BLOCK_ENTRY_TOKEN {
|
||||
mark := token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_BLOCK_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_BLOCK_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
|
||||
return yaml_parser_parse_node(parser, event, true, false)
|
||||
} else {
|
||||
parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, mark)
|
||||
}
|
||||
} else if token.token_type == yaml_BLOCK_END_TOKEN {
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
return true
|
||||
} else {
|
||||
mark := parser.marks[len(parser.marks)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
return yaml_parser_set_parser_error_context(parser,
|
||||
"while parsing a block collection", mark,
|
||||
"did not find expected '-' indicator", token.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* indentless_sequence ::= (BLOCK-ENTRY block_node?)+
|
||||
* *********** *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t,
|
||||
event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_BLOCK_ENTRY_TOKEN {
|
||||
mark := token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_BLOCK_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_KEY_TOKEN &&
|
||||
token.token_type != yaml_VALUE_TOKEN &&
|
||||
token.token_type != yaml_BLOCK_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE)
|
||||
return yaml_parser_parse_node(parser, event, true, false)
|
||||
} else {
|
||||
parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, mark)
|
||||
}
|
||||
} else {
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.start_mark,
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* block_mapping ::= BLOCK-MAPPING_START
|
||||
* *******************
|
||||
* ((KEY block_node_or_indentless_sequence?)?
|
||||
* *** *
|
||||
* (VALUE block_node_or_indentless_sequence?)?)*
|
||||
*
|
||||
* BLOCK-END
|
||||
* *********
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_KEY_TOKEN {
|
||||
mark := token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_KEY_TOKEN &&
|
||||
token.token_type != yaml_VALUE_TOKEN &&
|
||||
token.token_type != yaml_BLOCK_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE)
|
||||
return yaml_parser_parse_node(parser, event, true, true)
|
||||
} else {
|
||||
parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, mark)
|
||||
}
|
||||
} else if token.token_type == yaml_BLOCK_END_TOKEN {
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
skip_token(parser)
|
||||
return true
|
||||
} else {
|
||||
mark := parser.marks[len(parser.marks)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
return yaml_parser_set_parser_error_context(parser,
|
||||
"while parsing a block mapping", mark,
|
||||
"did not find expected key", token.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* block_mapping ::= BLOCK-MAPPING_START
|
||||
*
|
||||
* ((KEY block_node_or_indentless_sequence?)?
|
||||
*
|
||||
* (VALUE block_node_or_indentless_sequence?)?)*
|
||||
* ***** *
|
||||
* BLOCK-END
|
||||
*
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t,
|
||||
event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_VALUE_TOKEN {
|
||||
mark := token.end_mark
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_KEY_TOKEN &&
|
||||
token.token_type != yaml_VALUE_TOKEN &&
|
||||
token.token_type != yaml_BLOCK_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE)
|
||||
return yaml_parser_parse_node(parser, event, true, true)
|
||||
} else {
|
||||
parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, mark)
|
||||
}
|
||||
} else {
|
||||
parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_sequence ::= FLOW-SEQUENCE-START
|
||||
* *******************
|
||||
* (flow_sequence_entry FLOW-ENTRY)*
|
||||
* * **********
|
||||
* flow_sequence_entry?
|
||||
* *
|
||||
* FLOW-SEQUENCE-END
|
||||
* *****************
|
||||
* flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type != yaml_FLOW_SEQUENCE_END_TOKEN {
|
||||
if !first {
|
||||
if token.token_type == yaml_FLOW_ENTRY_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
mark := parser.marks[len(parser.marks)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
return yaml_parser_set_parser_error_context(parser,
|
||||
"while parsing a flow sequence", mark,
|
||||
"did not find expected ',' or ']'", token.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
if token.token_type == yaml_KEY_TOKEN {
|
||||
parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_START_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
implicit: true,
|
||||
style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
return true
|
||||
} else if token.token_type != yaml_FLOW_SEQUENCE_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SEQUENCE_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* *** *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t,
|
||||
event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type != yaml_VALUE_TOKEN &&
|
||||
token.token_type != yaml_FLOW_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_FLOW_SEQUENCE_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
} else {
|
||||
mark := token.end_mark
|
||||
skip_token(parser)
|
||||
parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, mark)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* ***** *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t,
|
||||
event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type == yaml_VALUE_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_FLOW_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_FLOW_SEQUENCE_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
}
|
||||
}
|
||||
parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t,
|
||||
event *yaml_event_t) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.start_mark,
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_mapping ::= FLOW-MAPPING-START
|
||||
* ******************
|
||||
* (flow_mapping_entry FLOW-ENTRY)*
|
||||
* * **********
|
||||
* flow_mapping_entry?
|
||||
* ******************
|
||||
* FLOW-MAPPING-END
|
||||
* ****************
|
||||
* flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* * *** *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t,
|
||||
event *yaml_event_t, first bool) bool {
|
||||
if first {
|
||||
token := peek_token(parser)
|
||||
parser.marks = append(parser.marks, token.start_mark)
|
||||
skip_token(parser)
|
||||
}
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if token.token_type != yaml_FLOW_MAPPING_END_TOKEN {
|
||||
if !first {
|
||||
if token.token_type == yaml_FLOW_ENTRY_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
mark := parser.marks[len(parser.marks)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
|
||||
return yaml_parser_set_parser_error_context(parser,
|
||||
"while parsing a flow mapping", mark,
|
||||
"did not find expected ',' or '}'", token.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
if token.token_type == yaml_KEY_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_VALUE_TOKEN &&
|
||||
token.token_type != yaml_FLOW_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_FLOW_MAPPING_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
} else {
|
||||
parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event,
|
||||
token.start_mark)
|
||||
}
|
||||
} else if token.token_type != yaml_FLOW_MAPPING_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
parser.state = parser.states[len(parser.states)-1]
|
||||
parser.states = parser.states[:len(parser.states)-1]
|
||||
parser.marks = parser.marks[:len(parser.marks)-1]
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_MAPPING_END_EVENT,
|
||||
start_mark: token.start_mark,
|
||||
end_mark: token.end_mark,
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the productions:
|
||||
* flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
|
||||
* * ***** *
|
||||
*/
|
||||
|
||||
func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t,
|
||||
event *yaml_event_t, empty bool) bool {
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if empty {
|
||||
parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event,
|
||||
token.start_mark)
|
||||
}
|
||||
|
||||
if token.token_type == yaml_VALUE_TOKEN {
|
||||
skip_token(parser)
|
||||
token = peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if token.token_type != yaml_FLOW_ENTRY_TOKEN &&
|
||||
token.token_type != yaml_FLOW_MAPPING_END_TOKEN {
|
||||
parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE)
|
||||
return yaml_parser_parse_node(parser, event, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
|
||||
return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an empty scalar event.
|
||||
*/
|
||||
|
||||
func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t,
|
||||
mark YAML_mark_t) bool {
|
||||
*event = yaml_event_t{
|
||||
event_type: yaml_SCALAR_EVENT,
|
||||
start_mark: mark,
|
||||
end_mark: mark,
|
||||
value: nil,
|
||||
implicit: true,
|
||||
style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse directives.
|
||||
*/
|
||||
|
||||
func yaml_parser_process_directives(parser *yaml_parser_t,
|
||||
version_directive_ref **yaml_version_directive_t,
|
||||
tag_directives_ref *[]yaml_tag_directive_t) bool {
|
||||
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var version_directive *yaml_version_directive_t
|
||||
var tag_directives []yaml_tag_directive_t
|
||||
|
||||
for token.token_type == yaml_VERSION_DIRECTIVE_TOKEN ||
|
||||
token.token_type == yaml_TAG_DIRECTIVE_TOKEN {
|
||||
if token.token_type == yaml_VERSION_DIRECTIVE_TOKEN {
|
||||
if version_directive != nil {
|
||||
yaml_parser_set_parser_error(parser,
|
||||
"found duplicate %YAML directive", token.start_mark)
|
||||
return false
|
||||
}
|
||||
if token.major != 1 ||
|
||||
token.minor != 1 {
|
||||
yaml_parser_set_parser_error(parser,
|
||||
"found incompatible YAML document", token.start_mark)
|
||||
return false
|
||||
}
|
||||
version_directive = &yaml_version_directive_t{
|
||||
major: token.major,
|
||||
minor: token.minor,
|
||||
}
|
||||
} else if token.token_type == yaml_TAG_DIRECTIVE_TOKEN {
|
||||
value := yaml_tag_directive_t{
|
||||
handle: token.value,
|
||||
prefix: token.prefix,
|
||||
}
|
||||
|
||||
if !yaml_parser_append_tag_directive(parser, value, false,
|
||||
token.start_mark) {
|
||||
return false
|
||||
}
|
||||
tag_directives = append(tag_directives, value)
|
||||
}
|
||||
|
||||
skip_token(parser)
|
||||
token := peek_token(parser)
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for i := range default_tag_directives {
|
||||
if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if version_directive_ref != nil {
|
||||
*version_directive_ref = version_directive
|
||||
}
|
||||
if tag_directives_ref != nil {
|
||||
*tag_directives_ref = tag_directives
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Append a tag directive to the directives stack.
|
||||
*/
|
||||
|
||||
func yaml_parser_append_tag_directive(parser *yaml_parser_t,
|
||||
value yaml_tag_directive_t, allow_duplicates bool, mark YAML_mark_t) bool {
|
||||
for i := range parser.tag_directives {
|
||||
tag := &parser.tag_directives[i]
|
||||
if bytes.Equal(value.handle, tag.handle) {
|
||||
if allow_duplicates {
|
||||
return true
|
||||
}
|
||||
return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark)
|
||||
}
|
||||
}
|
||||
|
||||
parser.tag_directives = append(parser.tag_directives, value)
|
||||
return true
|
||||
}
|
|
@ -1,465 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
* Set the reader error and return 0.
|
||||
*/
|
||||
|
||||
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string,
|
||||
offset int, value int) bool {
|
||||
parser.error = yaml_READER_ERROR
|
||||
parser.problem = problem
|
||||
parser.problem_offset = offset
|
||||
parser.problem_value = value
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Byte order marks.
|
||||
*/
|
||||
const (
|
||||
BOM_UTF8 = "\xef\xbb\xbf"
|
||||
BOM_UTF16LE = "\xff\xfe"
|
||||
BOM_UTF16BE = "\xfe\xff"
|
||||
)
|
||||
|
||||
/*
|
||||
* Determine the input stream encoding by checking the BOM symbol. If no BOM is
|
||||
* found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
|
||||
*/
|
||||
|
||||
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
|
||||
/* Ensure that we had enough bytes in the raw buffer. */
|
||||
for !parser.eof &&
|
||||
len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine the encoding. */
|
||||
raw := parser.raw_buffer
|
||||
pos := parser.raw_buffer_pos
|
||||
remaining := len(raw) - pos
|
||||
if remaining >= 2 &&
|
||||
raw[pos] == BOM_UTF16LE[0] && raw[pos+1] == BOM_UTF16LE[1] {
|
||||
parser.encoding = yaml_UTF16LE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if remaining >= 2 &&
|
||||
raw[pos] == BOM_UTF16BE[0] && raw[pos+1] == BOM_UTF16BE[1] {
|
||||
parser.encoding = yaml_UTF16BE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if remaining >= 3 &&
|
||||
raw[pos] == BOM_UTF8[0] && raw[pos+1] == BOM_UTF8[1] && raw[pos+2] == BOM_UTF8[2] {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
parser.raw_buffer_pos += 3
|
||||
parser.offset += 3
|
||||
} else {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the raw buffer.
|
||||
*/
|
||||
|
||||
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
|
||||
size_read := 0
|
||||
|
||||
/* Return if the raw buffer is full. */
|
||||
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Return on EOF. */
|
||||
|
||||
if parser.eof {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Move the remaining bytes in the raw buffer to the beginning. */
|
||||
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
|
||||
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
|
||||
}
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
|
||||
parser.raw_buffer_pos = 0
|
||||
|
||||
/* Call the read handler to fill the buffer. */
|
||||
size_read, err := parser.read_handler(parser,
|
||||
parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
|
||||
|
||||
if err == io.EOF {
|
||||
parser.eof = true
|
||||
} else if err != nil {
|
||||
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(),
|
||||
parser.offset, -1)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the buffer contains at least `length` characters.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*
|
||||
* The length is supposed to be significantly less that the buffer size.
|
||||
*/
|
||||
|
||||
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
|
||||
/* Read handler must be set. */
|
||||
if parser.read_handler == nil {
|
||||
panic("read handler must be set")
|
||||
}
|
||||
|
||||
/* If the EOF flag is set and the raw buffer is empty, do nothing. */
|
||||
|
||||
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Return if the buffer contains enough characters. */
|
||||
|
||||
if parser.unread >= length {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Determine the input encoding if it is not known yet. */
|
||||
|
||||
if parser.encoding == yaml_ANY_ENCODING {
|
||||
if !yaml_parser_determine_encoding(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/* Move the unread characters to the beginning of the buffer. */
|
||||
buffer_end := len(parser.buffer)
|
||||
if 0 < parser.buffer_pos &&
|
||||
parser.buffer_pos < buffer_end {
|
||||
copy(parser.buffer, parser.buffer[parser.buffer_pos:])
|
||||
buffer_end -= parser.buffer_pos
|
||||
parser.buffer_pos = 0
|
||||
} else if parser.buffer_pos == buffer_end {
|
||||
buffer_end = 0
|
||||
parser.buffer_pos = 0
|
||||
}
|
||||
|
||||
parser.buffer = parser.buffer[:cap(parser.buffer)]
|
||||
|
||||
/* Fill the buffer until it has enough characters. */
|
||||
first := true
|
||||
for parser.unread < length {
|
||||
/* Fill the raw buffer if necessary. */
|
||||
|
||||
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
parser.buffer = parser.buffer[:buffer_end]
|
||||
return false
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
/* Decode the raw buffer. */
|
||||
for parser.raw_buffer_pos != len(parser.raw_buffer) {
|
||||
var value rune
|
||||
var w int
|
||||
|
||||
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
|
||||
incomplete := false
|
||||
|
||||
/* Decode the next character. */
|
||||
|
||||
switch parser.encoding {
|
||||
case yaml_UTF8_ENCODING:
|
||||
|
||||
/*
|
||||
* Decode a UTF-8 character. Check RFC 3629
|
||||
* (http://www.ietf.org/rfc/rfc3629.txt) for more details.
|
||||
*
|
||||
* The following table (taken from the RFC) is used for
|
||||
* decoding.
|
||||
*
|
||||
* Char. number range | UTF-8 octet sequence
|
||||
* (hexadecimal) | (binary)
|
||||
* --------------------+------------------------------------
|
||||
* 0000 0000-0000 007F | 0xxxxxxx
|
||||
* 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
* 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
* 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
*
|
||||
* Additionally, the characters in the range 0xD800-0xDFFF
|
||||
* are prohibited as they are reserved for use with UTF-16
|
||||
* surrogate pairs.
|
||||
*/
|
||||
|
||||
/* Determine the length of the UTF-8 sequence. */
|
||||
|
||||
octet := parser.raw_buffer[parser.raw_buffer_pos]
|
||||
w = width(octet)
|
||||
|
||||
/* Check if the leading octet is valid. */
|
||||
|
||||
if w == 0 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid leading UTF-8 octet",
|
||||
parser.offset, int(octet))
|
||||
}
|
||||
|
||||
/* Check if the raw buffer contains an incomplete character. */
|
||||
|
||||
if w > raw_unread {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-8 octet sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
|
||||
/* Decode the leading octet. */
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
value = rune(octet & 0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
value = rune(octet & 0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
value = rune(octet & 0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
value = rune(octet & 0x07)
|
||||
default:
|
||||
value = 0
|
||||
}
|
||||
|
||||
/* Check and decode the trailing octets. */
|
||||
|
||||
for k := 1; k < w; k++ {
|
||||
octet = parser.raw_buffer[parser.raw_buffer_pos+k]
|
||||
|
||||
/* Check if the octet is valid. */
|
||||
|
||||
if (octet & 0xC0) != 0x80 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid trailing UTF-8 octet",
|
||||
parser.offset+k, int(octet))
|
||||
}
|
||||
|
||||
/* Decode the octet. */
|
||||
|
||||
value = (value << 6) + rune(octet&0x3F)
|
||||
}
|
||||
|
||||
/* Check the length of the sequence against the value. */
|
||||
switch {
|
||||
case w == 1:
|
||||
case w == 2 && value >= 0x80:
|
||||
case w == 3 && value >= 0x800:
|
||||
case w == 4 && value >= 0x10000:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid length of a UTF-8 sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
|
||||
/* Check the range of the value. */
|
||||
|
||||
if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid Unicode character",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
case yaml_UTF16LE_ENCODING,
|
||||
yaml_UTF16BE_ENCODING:
|
||||
|
||||
var low, high int
|
||||
if parser.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
high, low = 1, 0
|
||||
}
|
||||
|
||||
/*
|
||||
* The UTF-16 encoding is not as simple as one might
|
||||
* naively think. Check RFC 2781
|
||||
* (http://www.ietf.org/rfc/rfc2781.txt).
|
||||
*
|
||||
* Normally, two subsequent bytes describe a Unicode
|
||||
* character. However a special technique (called a
|
||||
* surrogate pair) is used for specifying character
|
||||
* values larger than 0xFFFF.
|
||||
*
|
||||
* A surrogate pair consists of two pseudo-characters:
|
||||
* high surrogate area (0xD800-0xDBFF)
|
||||
* low surrogate area (0xDC00-0xDFFF)
|
||||
*
|
||||
* The following formulas are used for decoding
|
||||
* and encoding characters using surrogate pairs:
|
||||
*
|
||||
* U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
|
||||
* U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
|
||||
* W1 = 110110yyyyyyyyyy
|
||||
* W2 = 110111xxxxxxxxxx
|
||||
*
|
||||
* where U is the character value, W1 is the high surrogate
|
||||
* area, W2 is the low surrogate area.
|
||||
*/
|
||||
|
||||
/* Check for incomplete UTF-16 character. */
|
||||
|
||||
if raw_unread < 2 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 character",
|
||||
parser.offset, -1)
|
||||
}
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
|
||||
/* Get the character. */
|
||||
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
|
||||
|
||||
/* Check for unexpected low surrogate area. */
|
||||
|
||||
if (value & 0xFC00) == 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"unexpected low surrogate area",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
/* Check for a high surrogate area. */
|
||||
|
||||
if (value & 0xFC00) == 0xD800 {
|
||||
|
||||
w = 4
|
||||
|
||||
/* Check for incomplete surrogate pair. */
|
||||
|
||||
if raw_unread < 4 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 surrogate pair",
|
||||
parser.offset, -1)
|
||||
}
|
||||
incomplete = true
|
||||
break
|
||||
}
|
||||
|
||||
/* Get the next character. */
|
||||
|
||||
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
|
||||
|
||||
/* Check for a low surrogate area. */
|
||||
|
||||
if (value2 & 0xFC00) != 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"expected low surrogate area",
|
||||
parser.offset+2, int(value2))
|
||||
}
|
||||
|
||||
/* Generate the value of the surrogate pair. */
|
||||
|
||||
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
|
||||
} else {
|
||||
w = 2
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
panic("Impossible") /* Impossible. */
|
||||
}
|
||||
|
||||
/* Check if the raw buffer contains enough bytes to form a character. */
|
||||
|
||||
if incomplete {
|
||||
break
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the character is in the allowed range:
|
||||
* #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
|
||||
* | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
|
||||
* | [#x10000-#x10FFFF] (32 bit)
|
||||
*/
|
||||
|
||||
if !(value == 0x09 || value == 0x0A || value == 0x0D ||
|
||||
(value >= 0x20 && value <= 0x7E) ||
|
||||
(value == 0x85) || (value >= 0xA0 && value <= 0xD7FF) ||
|
||||
(value >= 0xE000 && value <= 0xFFFD) ||
|
||||
(value >= 0x10000 && value <= 0x10FFFF)) {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"control characters are not allowed",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
/* Move the raw pointers. */
|
||||
|
||||
parser.raw_buffer_pos += w
|
||||
parser.offset += w
|
||||
|
||||
/* Finally put the character into the buffer. */
|
||||
|
||||
/* 0000 0000-0000 007F . 0xxxxxxx */
|
||||
if value <= 0x7F {
|
||||
parser.buffer[buffer_end] = byte(value)
|
||||
} else if value <= 0x7FF {
|
||||
/* 0000 0080-0000 07FF . 110xxxxx 10xxxxxx */
|
||||
parser.buffer[buffer_end] = byte(0xC0 + (value >> 6))
|
||||
parser.buffer[buffer_end+1] = byte(0x80 + (value & 0x3F))
|
||||
} else if value <= 0xFFFF {
|
||||
/* 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx */
|
||||
parser.buffer[buffer_end] = byte(0xE0 + (value >> 12))
|
||||
parser.buffer[buffer_end+1] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_end+2] = byte(0x80 + (value & 0x3F))
|
||||
} else {
|
||||
/* 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
|
||||
parser.buffer[buffer_end] = byte(0xF0 + (value >> 18))
|
||||
parser.buffer[buffer_end+1] = byte(0x80 + ((value >> 12) & 0x3F))
|
||||
parser.buffer[buffer_end+2] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_end+3] = byte(0x80 + (value & 0x3F))
|
||||
}
|
||||
|
||||
buffer_end += w
|
||||
parser.unread++
|
||||
}
|
||||
|
||||
/* On EOF, put NUL into the buffer and return. */
|
||||
|
||||
if parser.eof {
|
||||
parser.buffer[buffer_end] = 0
|
||||
buffer_end++
|
||||
parser.buffer = parser.buffer[:buffer_end]
|
||||
parser.unread++
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parser.buffer = parser.buffer[:buffer_end]
|
||||
return true
|
||||
}
|
|
@ -1,449 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var byteSliceType = reflect.TypeOf([]byte(nil))
|
||||
|
||||
var binary_tags = [][]byte{[]byte("!binary"), []byte(yaml_BINARY_TAG)}
|
||||
var bool_values map[string]bool
|
||||
var null_values map[string]bool
|
||||
|
||||
var signs = []byte{'-', '+'}
|
||||
var nulls = []byte{'~', 'n', 'N'}
|
||||
var bools = []byte{'t', 'T', 'f', 'F', 'y', 'Y', 'n', 'N', 'o', 'O'}
|
||||
|
||||
var timestamp_regexp *regexp.Regexp
|
||||
var ymd_regexp *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
bool_values = make(map[string]bool)
|
||||
bool_values["y"] = true
|
||||
bool_values["yes"] = true
|
||||
bool_values["n"] = false
|
||||
bool_values["no"] = false
|
||||
bool_values["true"] = true
|
||||
bool_values["false"] = false
|
||||
bool_values["on"] = true
|
||||
bool_values["off"] = false
|
||||
|
||||
null_values = make(map[string]bool)
|
||||
null_values["~"] = true
|
||||
null_values["null"] = true
|
||||
null_values["Null"] = true
|
||||
null_values["NULL"] = true
|
||||
|
||||
timestamp_regexp = regexp.MustCompile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(?:Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?))?)?$")
|
||||
ymd_regexp = regexp.MustCompile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$")
|
||||
}
|
||||
|
||||
func resolve(event yaml_event_t, v reflect.Value, useNumber bool) (string, error) {
|
||||
val := string(event.value)
|
||||
|
||||
if null_values[val] {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
return yaml_NULL_TAG, nil
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
if useNumber && v.Type() == numberType {
|
||||
tag, i := resolveInterface(event, useNumber)
|
||||
if n, ok := i.(Number); ok {
|
||||
v.Set(reflect.ValueOf(n))
|
||||
return tag, nil
|
||||
}
|
||||
return "", fmt.Errorf("Not a number: '%s' at %s", event.value, event.start_mark)
|
||||
}
|
||||
|
||||
return resolve_string(val, v, event)
|
||||
case reflect.Bool:
|
||||
return resolve_bool(val, v, event)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return resolve_int(val, v, useNumber, event)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return resolve_uint(val, v, useNumber, event)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return resolve_float(val, v, useNumber, event)
|
||||
case reflect.Interface:
|
||||
_, i := resolveInterface(event, useNumber)
|
||||
if i != nil {
|
||||
v.Set(reflect.ValueOf(i))
|
||||
} else {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
return resolve_time(val, v, event)
|
||||
case reflect.Slice:
|
||||
if v.Type() != byteSliceType {
|
||||
return "", fmt.Errorf("Cannot resolve %s into %s at %s", val, v.String(), event.start_mark)
|
||||
}
|
||||
b, err := decode_binary(event.value, event)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
v.Set(reflect.ValueOf(b))
|
||||
default:
|
||||
return "", fmt.Errorf("Unknown resolution for '%s' using %s at %s", val, v.String(), event.start_mark)
|
||||
}
|
||||
|
||||
return yaml_STR_TAG, nil
|
||||
}
|
||||
|
||||
func hasBinaryTag(event yaml_event_t) bool {
|
||||
for _, tag := range binary_tags {
|
||||
if bytes.Equal(event.tag, tag) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func decode_binary(value []byte, event yaml_event_t) ([]byte, error) {
|
||||
b := make([]byte, base64.StdEncoding.DecodedLen(len(value)))
|
||||
n, err := base64.StdEncoding.Decode(b, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid base64 text: '%s' at %s", string(b), event.start_mark)
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
|
||||
func resolve_string(val string, v reflect.Value, event yaml_event_t) (string, error) {
|
||||
if len(event.tag) > 0 {
|
||||
if hasBinaryTag(event) {
|
||||
b, err := decode_binary(event.value, event)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
val = string(b)
|
||||
}
|
||||
}
|
||||
v.SetString(val)
|
||||
return yaml_STR_TAG, nil
|
||||
}
|
||||
|
||||
func resolve_bool(val string, v reflect.Value, event yaml_event_t) (string, error) {
|
||||
b, found := bool_values[strings.ToLower(val)]
|
||||
if !found {
|
||||
return "", fmt.Errorf("Invalid boolean: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
|
||||
v.SetBool(b)
|
||||
return yaml_BOOL_TAG, nil
|
||||
}
|
||||
|
||||
func resolve_int(val string, v reflect.Value, useNumber bool, event yaml_event_t) (string, error) {
|
||||
original := val
|
||||
val = strings.Replace(val, "_", "", -1)
|
||||
var value uint64
|
||||
|
||||
isNumberValue := v.Type() == numberType
|
||||
|
||||
sign := int64(1)
|
||||
if val[0] == '-' {
|
||||
sign = -1
|
||||
val = val[1:]
|
||||
} else if val[0] == '+' {
|
||||
val = val[1:]
|
||||
}
|
||||
|
||||
base := 0
|
||||
if val == "0" {
|
||||
if isNumberValue {
|
||||
v.SetString("0")
|
||||
} else {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
}
|
||||
|
||||
return yaml_INT_TAG, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(val, "0o") {
|
||||
base = 8
|
||||
val = val[2:]
|
||||
}
|
||||
|
||||
value, err := strconv.ParseUint(val, base, 64)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid integer: '%s' at %s", original, event.start_mark)
|
||||
}
|
||||
|
||||
var val64 int64
|
||||
if value <= math.MaxInt64 {
|
||||
val64 = int64(value)
|
||||
if sign == -1 {
|
||||
val64 = -val64
|
||||
}
|
||||
} else if sign == -1 && value == uint64(math.MaxInt64)+1 {
|
||||
val64 = math.MinInt64
|
||||
} else {
|
||||
return "", fmt.Errorf("Invalid integer: '%s' at %s", original, event.start_mark)
|
||||
}
|
||||
|
||||
if isNumberValue {
|
||||
v.SetString(strconv.FormatInt(val64, 10))
|
||||
} else {
|
||||
if v.OverflowInt(val64) {
|
||||
return "", fmt.Errorf("Invalid integer: '%s' at %s", original, event.start_mark)
|
||||
}
|
||||
v.SetInt(val64)
|
||||
}
|
||||
|
||||
return yaml_INT_TAG, nil
|
||||
}
|
||||
|
||||
func resolve_uint(val string, v reflect.Value, useNumber bool, event yaml_event_t) (string, error) {
|
||||
original := val
|
||||
val = strings.Replace(val, "_", "", -1)
|
||||
var value uint64
|
||||
|
||||
isNumberValue := v.Type() == numberType
|
||||
|
||||
if val[0] == '-' {
|
||||
return "", fmt.Errorf("Unsigned int with negative value: '%s' at %s", original, event.start_mark)
|
||||
}
|
||||
|
||||
if val[0] == '+' {
|
||||
val = val[1:]
|
||||
}
|
||||
|
||||
base := 0
|
||||
if val == "0" {
|
||||
if isNumberValue {
|
||||
v.SetString("0")
|
||||
} else {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
}
|
||||
|
||||
return yaml_INT_TAG, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(val, "0o") {
|
||||
base = 8
|
||||
val = val[2:]
|
||||
}
|
||||
|
||||
value, err := strconv.ParseUint(val, base, 64)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid unsigned integer: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
|
||||
if isNumberValue {
|
||||
v.SetString(strconv.FormatUint(value, 10))
|
||||
} else {
|
||||
if v.OverflowUint(value) {
|
||||
return "", fmt.Errorf("Invalid unsigned integer: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
|
||||
v.SetUint(value)
|
||||
}
|
||||
|
||||
return yaml_INT_TAG, nil
|
||||
}
|
||||
|
||||
func resolve_float(val string, v reflect.Value, useNumber bool, event yaml_event_t) (string, error) {
|
||||
val = strings.Replace(val, "_", "", -1)
|
||||
var value float64
|
||||
|
||||
isNumberValue := v.Type() == numberType
|
||||
typeBits := 64
|
||||
if !isNumberValue {
|
||||
typeBits = v.Type().Bits()
|
||||
}
|
||||
|
||||
sign := 1
|
||||
if val[0] == '-' {
|
||||
sign = -1
|
||||
val = val[1:]
|
||||
} else if val[0] == '+' {
|
||||
val = val[1:]
|
||||
}
|
||||
|
||||
valLower := strings.ToLower(val)
|
||||
if valLower == ".inf" {
|
||||
value = math.Inf(sign)
|
||||
} else if valLower == ".nan" {
|
||||
value = math.NaN()
|
||||
} else {
|
||||
var err error
|
||||
value, err = strconv.ParseFloat(val, typeBits)
|
||||
value *= float64(sign)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Invalid float: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
}
|
||||
|
||||
if isNumberValue {
|
||||
v.SetString(strconv.FormatFloat(value, 'g', -1, typeBits))
|
||||
} else {
|
||||
if v.OverflowFloat(value) {
|
||||
return "", fmt.Errorf("Invalid float: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
|
||||
v.SetFloat(value)
|
||||
}
|
||||
|
||||
return yaml_FLOAT_TAG, nil
|
||||
}
|
||||
|
||||
func resolve_time(val string, v reflect.Value, event yaml_event_t) (string, error) {
|
||||
var parsedTime time.Time
|
||||
matches := ymd_regexp.FindStringSubmatch(val)
|
||||
if len(matches) > 0 {
|
||||
year, _ := strconv.Atoi(matches[1])
|
||||
month, _ := strconv.Atoi(matches[2])
|
||||
day, _ := strconv.Atoi(matches[3])
|
||||
parsedTime = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||
} else {
|
||||
matches = timestamp_regexp.FindStringSubmatch(val)
|
||||
if len(matches) == 0 {
|
||||
return "", fmt.Errorf("Invalid timestamp: '%s' at %s", val, event.start_mark)
|
||||
}
|
||||
|
||||
year, _ := strconv.Atoi(matches[1])
|
||||
month, _ := strconv.Atoi(matches[2])
|
||||
day, _ := strconv.Atoi(matches[3])
|
||||
hour, _ := strconv.Atoi(matches[4])
|
||||
min, _ := strconv.Atoi(matches[5])
|
||||
sec, _ := strconv.Atoi(matches[6])
|
||||
|
||||
nsec := 0
|
||||
if matches[7] != "" {
|
||||
millis, _ := strconv.Atoi(matches[7])
|
||||
nsec = int(time.Duration(millis) * time.Millisecond)
|
||||
}
|
||||
|
||||
loc := time.UTC
|
||||
if matches[8] != "" {
|
||||
sign := matches[8][0]
|
||||
hr, _ := strconv.Atoi(matches[8][1:])
|
||||
min := 0
|
||||
if matches[9] != "" {
|
||||
min, _ = strconv.Atoi(matches[9])
|
||||
}
|
||||
|
||||
zoneOffset := (hr*60 + min) * 60
|
||||
if sign == '-' {
|
||||
zoneOffset = -zoneOffset
|
||||
}
|
||||
|
||||
loc = time.FixedZone("", zoneOffset)
|
||||
}
|
||||
parsedTime = time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc)
|
||||
}
|
||||
|
||||
v.Set(reflect.ValueOf(parsedTime))
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func resolveInterface(event yaml_event_t, useNumber bool) (string, interface{}) {
|
||||
val := string(event.value)
|
||||
if len(event.tag) == 0 && !event.implicit {
|
||||
return "", val
|
||||
}
|
||||
|
||||
if len(val) == 0 {
|
||||
return yaml_NULL_TAG, nil
|
||||
}
|
||||
|
||||
var result interface{}
|
||||
|
||||
sign := false
|
||||
c := val[0]
|
||||
switch {
|
||||
case bytes.IndexByte(signs, c) != -1:
|
||||
sign = true
|
||||
fallthrough
|
||||
case c >= '0' && c <= '9':
|
||||
i := int64(0)
|
||||
result = &i
|
||||
if useNumber {
|
||||
var n Number
|
||||
result = &n
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(result).Elem()
|
||||
if _, err := resolve_int(val, v, useNumber, event); err == nil {
|
||||
return yaml_INT_TAG, v.Interface()
|
||||
}
|
||||
|
||||
f := float64(0)
|
||||
result = &f
|
||||
if useNumber {
|
||||
var n Number
|
||||
result = &n
|
||||
}
|
||||
|
||||
v = reflect.ValueOf(result).Elem()
|
||||
if _, err := resolve_float(val, v, useNumber, event); err == nil {
|
||||
return yaml_FLOAT_TAG, v.Interface()
|
||||
}
|
||||
|
||||
if !sign {
|
||||
t := time.Time{}
|
||||
if _, err := resolve_time(val, reflect.ValueOf(&t).Elem(), event); err == nil {
|
||||
return "", t
|
||||
}
|
||||
}
|
||||
case bytes.IndexByte(nulls, c) != -1:
|
||||
if null_values[val] {
|
||||
return yaml_NULL_TAG, nil
|
||||
}
|
||||
b := false
|
||||
if _, err := resolve_bool(val, reflect.ValueOf(&b).Elem(), event); err == nil {
|
||||
return yaml_BOOL_TAG, b
|
||||
}
|
||||
case c == '.':
|
||||
f := float64(0)
|
||||
result = &f
|
||||
if useNumber {
|
||||
var n Number
|
||||
result = &n
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(result).Elem()
|
||||
if _, err := resolve_float(val, v, useNumber, event); err == nil {
|
||||
return yaml_FLOAT_TAG, v.Interface()
|
||||
}
|
||||
case bytes.IndexByte(bools, c) != -1:
|
||||
b := false
|
||||
if _, err := resolve_bool(val, reflect.ValueOf(&b).Elem(), event); err == nil {
|
||||
return yaml_BOOL_TAG, b
|
||||
}
|
||||
}
|
||||
|
||||
if hasBinaryTag(event) {
|
||||
bytes, err := decode_binary(event.value, event)
|
||||
if err == nil {
|
||||
return yaml_BINARY_TAG, bytes
|
||||
}
|
||||
}
|
||||
|
||||
return yaml_STR_TAG, val
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Run_parser(cmd string, args []string) {
|
||||
for i := 0; i < len(args); i++ {
|
||||
fmt.Printf("[%d] Scanning '%s'", i, args[i])
|
||||
file, err := os.Open(args[i])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Invalid file '%s': %s", args[i], err.Error()))
|
||||
}
|
||||
|
||||
parser := yaml_parser_t{}
|
||||
yaml_parser_initialize(&parser)
|
||||
yaml_parser_set_input_reader(&parser, file)
|
||||
|
||||
failed := false
|
||||
token := yaml_token_t{}
|
||||
count := 0
|
||||
for {
|
||||
if !yaml_parser_scan(&parser, &token) {
|
||||
failed = true
|
||||
break
|
||||
}
|
||||
|
||||
if token.token_type == yaml_STREAM_END_TOKEN {
|
||||
break
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
msg := "SUCCESS"
|
||||
if failed {
|
||||
msg = "FAILED"
|
||||
if parser.error != yaml_NO_ERROR {
|
||||
m := parser.problem_mark
|
||||
fmt.Printf("ERROR: (%s) %s @ line: %d col: %d\n",
|
||||
parser.context, parser.problem, m.line, m.column)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s (%d tokens)\n", msg, count)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,360 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// A field represents a single field found in a struct.
|
||||
type field struct {
|
||||
name string
|
||||
tag bool
|
||||
index []int
|
||||
typ reflect.Type
|
||||
omitEmpty bool
|
||||
flow bool
|
||||
}
|
||||
|
||||
// byName sorts field by name, breaking ties with depth,
|
||||
// then breaking ties with "name came from json tag", then
|
||||
// breaking ties with index sequence.
|
||||
type byName []field
|
||||
|
||||
func (x byName) Len() int { return len(x) }
|
||||
|
||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byName) Less(i, j int) bool {
|
||||
if x[i].name != x[j].name {
|
||||
return x[i].name < x[j].name
|
||||
}
|
||||
if len(x[i].index) != len(x[j].index) {
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
if x[i].tag != x[j].tag {
|
||||
return x[i].tag
|
||||
}
|
||||
return byIndex(x).Less(i, j)
|
||||
}
|
||||
|
||||
// byIndex sorts field by index sequence.
|
||||
type byIndex []field
|
||||
|
||||
func (x byIndex) Len() int { return len(x) }
|
||||
|
||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
|
||||
func (x byIndex) Less(i, j int) bool {
|
||||
for k, xik := range x[i].index {
|
||||
if k >= len(x[j].index) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].index[k] {
|
||||
return xik < x[j].index[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].index) < len(x[j].index)
|
||||
}
|
||||
|
||||
// typeFields returns a list of fields that JSON should recognize for the given type.
|
||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
||||
// and then any reachable anonymous structs.
|
||||
func typeFields(t reflect.Type) []field {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []field{}
|
||||
next := []field{{typ: t}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
count := map[reflect.Type]int{}
|
||||
nextCount := map[reflect.Type]int{}
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []field
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.typ] {
|
||||
continue
|
||||
}
|
||||
visited[f.typ] = true
|
||||
|
||||
// Scan f.typ for fields to include.
|
||||
for i := 0; i < f.typ.NumField(); i++ {
|
||||
sf := f.typ.Field(i)
|
||||
if sf.PkgPath != "" { // unexported
|
||||
continue
|
||||
}
|
||||
tag := sf.Tag.Get("yaml")
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if !isValidTag(name) {
|
||||
name = ""
|
||||
}
|
||||
index := make([]int, len(f.index)+1)
|
||||
copy(index, f.index)
|
||||
index[len(f.index)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
tagged := name != ""
|
||||
if name == "" {
|
||||
name = sf.Name
|
||||
}
|
||||
fields = append(fields, field{name, tagged, index, ft,
|
||||
opts.Contains("omitempty"), opts.Contains("flow")})
|
||||
if count[f.typ] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
next = append(next, field{name: ft.Name(), index: index, typ: ft})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byName(fields))
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with JSON tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.name
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
sort.Sort(byIndex(fields))
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// JSON tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []field) (field, bool) {
|
||||
// The fields are sorted in increasing index-length order. The winner
|
||||
// must therefore be one with the shortest index length. Drop all
|
||||
// longer entries, which is easy: just truncate the slice.
|
||||
length := len(fields[0].index)
|
||||
tagged := -1 // Index of first tagged field.
|
||||
for i, f := range fields {
|
||||
if len(f.index) > length {
|
||||
fields = fields[:i]
|
||||
break
|
||||
}
|
||||
if f.tag {
|
||||
if tagged >= 0 {
|
||||
// Multiple tagged fields at the same level: conflict.
|
||||
// Return no field.
|
||||
return field{}, false
|
||||
}
|
||||
tagged = i
|
||||
}
|
||||
}
|
||||
if tagged >= 0 {
|
||||
return fields[tagged], true
|
||||
}
|
||||
// All remaining fields have the same length. If there's more than one,
|
||||
// we have a conflict (two fields named "X" at the same level) and we
|
||||
// return no field.
|
||||
if len(fields) > 1 {
|
||||
return field{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
var fieldCache struct {
|
||||
sync.RWMutex
|
||||
m map[reflect.Type][]field
|
||||
}
|
||||
|
||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
||||
func cachedTypeFields(t reflect.Type) []field {
|
||||
fieldCache.RLock()
|
||||
f := fieldCache.m[t]
|
||||
fieldCache.RUnlock()
|
||||
if f != nil {
|
||||
return f
|
||||
}
|
||||
|
||||
// Compute fields without lock.
|
||||
// Might duplicate effort but won't hold other computations back.
|
||||
f = typeFields(t)
|
||||
if f == nil {
|
||||
f = []field{}
|
||||
}
|
||||
|
||||
fieldCache.Lock()
|
||||
if fieldCache.m == nil {
|
||||
fieldCache.m = map[reflect.Type][]field{}
|
||||
}
|
||||
fieldCache.m[t] = f
|
||||
fieldCache.Unlock()
|
||||
return f
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's "json"
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
default:
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||
for _, i := range index {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
v = v.Field(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func typeByIndex(t reflect.Type, index []int) reflect.Type {
|
||||
for _, i := range index {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
t = t.Field(i).Type
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||
// It implements the methods to sort by string.
|
||||
type stringValues []reflect.Value
|
||||
|
||||
func (sv stringValues) Len() int { return len(sv) }
|
||||
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||
func (sv stringValues) Less(i, j int) bool {
|
||||
av, ak := getElem(sv[i])
|
||||
bv, bk := getElem(sv[j])
|
||||
if ak == reflect.String && bk == reflect.String {
|
||||
return av.String() < bv.String()
|
||||
}
|
||||
|
||||
return ak < bk
|
||||
}
|
||||
|
||||
func getElem(v reflect.Value) (reflect.Value, reflect.Kind) {
|
||||
k := v.Kind()
|
||||
for k == reflect.Interface || k == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
k = v.Kind()
|
||||
}
|
||||
|
||||
return v, k
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
/*
|
||||
* Set the writer error and return 0.
|
||||
*/
|
||||
|
||||
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
|
||||
emitter.error = yaml_WRITER_ERROR
|
||||
emitter.problem = problem
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the output buffer.
|
||||
*/
|
||||
|
||||
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
|
||||
if emitter.write_handler == nil {
|
||||
panic("Write handler must be set") /* Write handler must be set. */
|
||||
}
|
||||
if emitter.encoding == yaml_ANY_ENCODING {
|
||||
panic("Encoding must be set") /* Output encoding must be set. */
|
||||
}
|
||||
|
||||
/* Check if the buffer is empty. */
|
||||
|
||||
if emitter.buffer_pos == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
/* If the output encoding is UTF-8, we don't need to recode the buffer. */
|
||||
|
||||
if emitter.encoding == yaml_UTF8_ENCODING {
|
||||
if err := emitter.write_handler(emitter,
|
||||
emitter.buffer[:emitter.buffer_pos]); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
return true
|
||||
}
|
||||
|
||||
/* Recode the buffer into the raw buffer. */
|
||||
|
||||
var low, high int
|
||||
if emitter.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
high, low = 1, 0
|
||||
}
|
||||
|
||||
pos := 0
|
||||
for pos < emitter.buffer_pos {
|
||||
|
||||
/*
|
||||
* See the "reader.c" code for more details on UTF-8 encoding. Note
|
||||
* that we assume that the buffer contains a valid UTF-8 sequence.
|
||||
*/
|
||||
|
||||
/* Read the next UTF-8 character. */
|
||||
|
||||
octet := emitter.buffer[pos]
|
||||
|
||||
var w int
|
||||
var value rune
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
w, value = 1, rune(octet&0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
w, value = 2, rune(octet&0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
w, value = 3, rune(octet&0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
w, value = 4, rune(octet&0x07)
|
||||
}
|
||||
|
||||
for k := 1; k < w; k++ {
|
||||
octet = emitter.buffer[pos+k]
|
||||
value = (value << 6) + (rune(octet) & 0x3F)
|
||||
}
|
||||
|
||||
pos += w
|
||||
|
||||
/* Write the character. */
|
||||
|
||||
if value < 0x10000 {
|
||||
var b [2]byte
|
||||
b[high] = byte(value >> 8)
|
||||
b[low] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
|
||||
} else {
|
||||
/* Write the character using a surrogate pair (check "reader.c"). */
|
||||
|
||||
var b [4]byte
|
||||
value -= 0x10000
|
||||
b[high] = byte(0xD8 + (value >> 18))
|
||||
b[low] = byte((value >> 10) & 0xFF)
|
||||
b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
|
||||
b[low+2] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the raw buffer. */
|
||||
|
||||
// Write the raw buffer.
|
||||
if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
|
||||
emitter.buffer_pos = 0
|
||||
emitter.raw_buffer = emitter.raw_buffer[:0]
|
||||
return true
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
const (
|
||||
yaml_VERSION_MAJOR = 0
|
||||
yaml_VERSION_MINOR = 1
|
||||
yaml_VERSION_PATCH = 6
|
||||
yaml_VERSION_STRING = "0.1.6"
|
||||
)
|
|
@ -1,891 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
const (
|
||||
INPUT_RAW_BUFFER_SIZE = 1024
|
||||
|
||||
/*
|
||||
* The size of the input buffer.
|
||||
*
|
||||
* It should be possible to decode the whole raw buffer.
|
||||
*/
|
||||
INPUT_BUFFER_SIZE = (INPUT_RAW_BUFFER_SIZE * 3)
|
||||
|
||||
/*
|
||||
* The size of the output buffer.
|
||||
*/
|
||||
|
||||
OUTPUT_BUFFER_SIZE = 512
|
||||
|
||||
/*
|
||||
* The size of the output raw buffer.
|
||||
*
|
||||
* It should be possible to encode the whole output buffer.
|
||||
*/
|
||||
|
||||
OUTPUT_RAW_BUFFER_SIZE = (OUTPUT_BUFFER_SIZE*2 + 2)
|
||||
|
||||
INITIAL_STACK_SIZE = 16
|
||||
INITIAL_QUEUE_SIZE = 16
|
||||
)
|
||||
|
||||
func width(b byte) int {
|
||||
if b&0x80 == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if b&0xE0 == 0xC0 {
|
||||
return 2
|
||||
}
|
||||
|
||||
if b&0xF0 == 0xE0 {
|
||||
return 3
|
||||
}
|
||||
|
||||
if b&0xF8 == 0xF0 {
|
||||
return 4
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func copy_bytes(dest []byte, dest_pos *int, src []byte, src_pos *int) {
|
||||
w := width(src[*src_pos])
|
||||
switch w {
|
||||
case 4:
|
||||
dest[*dest_pos+3] = src[*src_pos+3]
|
||||
fallthrough
|
||||
case 3:
|
||||
dest[*dest_pos+2] = src[*src_pos+2]
|
||||
fallthrough
|
||||
case 2:
|
||||
dest[*dest_pos+1] = src[*src_pos+1]
|
||||
fallthrough
|
||||
case 1:
|
||||
dest[*dest_pos] = src[*src_pos]
|
||||
default:
|
||||
panic("invalid width")
|
||||
}
|
||||
*dest_pos += w
|
||||
*src_pos += w
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is an alphabetical
|
||||
// * character, a digit, '_', or '-'.
|
||||
// */
|
||||
|
||||
func is_alpha(b byte) bool {
|
||||
return (b >= '0' && b <= '9') ||
|
||||
(b >= 'A' && b <= 'Z') ||
|
||||
(b >= 'a' && b <= 'z') ||
|
||||
b == '_' || b == '-'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is a digit.
|
||||
// */
|
||||
//
|
||||
func is_digit(b byte) bool {
|
||||
return b >= '0' && b <= '9'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Get the value of a digit.
|
||||
// */
|
||||
//
|
||||
func as_digit(b byte) int {
|
||||
return int(b) - '0'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is a hex-digit.
|
||||
// */
|
||||
//
|
||||
func is_hex(b byte) bool {
|
||||
return (b >= '0' && b <= '9') ||
|
||||
(b >= 'A' && b <= 'F') ||
|
||||
(b >= 'a' && b <= 'f')
|
||||
}
|
||||
|
||||
//
|
||||
// /*
|
||||
// * Get the value of a hex-digit.
|
||||
// */
|
||||
//
|
||||
func as_hex(b byte) int {
|
||||
if b >= 'A' && b <= 'F' {
|
||||
return int(b) - 'A' + 10
|
||||
} else if b >= 'a' && b <= 'f' {
|
||||
return int(b) - 'a' + 10
|
||||
}
|
||||
return int(b) - '0'
|
||||
}
|
||||
|
||||
// #define AS_HEX_AT(string,offset) \
|
||||
// (((string).pointer[offset] >= (yaml_char_t) 'A' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'F') ? \
|
||||
// ((string).pointer[offset] - (yaml_char_t) 'A' + 10) : \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'a' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'f') ? \
|
||||
// ((string).pointer[offset] - (yaml_char_t) 'a' + 10) : \
|
||||
// ((string).pointer[offset] - (yaml_char_t) '0'))
|
||||
|
||||
// /*
|
||||
// * Check if the character is a line break, space, tab, or NUL.
|
||||
// */
|
||||
func is_blankz_at(b []byte, i int) bool {
|
||||
return is_blank(b[i]) || is_breakz_at(b, i)
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is a line break.
|
||||
// */
|
||||
func is_break_at(b []byte, i int) bool {
|
||||
return b[i] == '\r' || /* CR (#xD)*/
|
||||
b[i] == '\n' || /* LF (#xA) */
|
||||
(b[i] == 0xC2 && b[i+1] == 0x85) || /* NEL (#x85) */
|
||||
(b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8) || /* LS (#x2028) */
|
||||
(b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) /* PS (#x2029) */
|
||||
}
|
||||
|
||||
func is_breakz_at(b []byte, i int) bool {
|
||||
return is_break_at(b, i) || is_z(b[i])
|
||||
}
|
||||
|
||||
func is_crlf_at(b []byte, i int) bool {
|
||||
return b[i] == '\r' && b[i+1] == '\n'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is NUL.
|
||||
// */
|
||||
func is_z(b byte) bool {
|
||||
return b == 0x0
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is space.
|
||||
// */
|
||||
func is_space(b byte) bool {
|
||||
return b == ' '
|
||||
}
|
||||
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is tab.
|
||||
// */
|
||||
func is_tab(b byte) bool {
|
||||
return b == '\t'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is blank (space or tab).
|
||||
// */
|
||||
func is_blank(b byte) bool {
|
||||
return is_space(b) || is_tab(b)
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character is ASCII.
|
||||
// */
|
||||
func is_ascii(b byte) bool {
|
||||
return b <= '\x7f'
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character can be printed unescaped.
|
||||
// */
|
||||
func is_printable_at(b []byte, i int) bool {
|
||||
return ((b[i] == 0x0A) || /* . == #x0A */
|
||||
(b[i] >= 0x20 && b[i] <= 0x7E) || /* #x20 <= . <= #x7E */
|
||||
(b[i] == 0xC2 && b[i+1] >= 0xA0) || /* #0xA0 <= . <= #xD7FF */
|
||||
(b[i] > 0xC2 && b[i] < 0xED) ||
|
||||
(b[i] == 0xED && b[i+1] < 0xA0) ||
|
||||
(b[i] == 0xEE) ||
|
||||
(b[i] == 0xEF && /* && . != #xFEFF */
|
||||
!(b[i+1] == 0xBB && b[i+2] == 0xBF) &&
|
||||
!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
|
||||
}
|
||||
|
||||
func insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
|
||||
// collapse the slice
|
||||
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
|
||||
if parser.tokens_head != len(parser.tokens) {
|
||||
// move the tokens down
|
||||
copy(parser.tokens, parser.tokens[parser.tokens_head:])
|
||||
}
|
||||
// readjust the length
|
||||
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
|
||||
parser.tokens_head = 0
|
||||
}
|
||||
|
||||
parser.tokens = append(parser.tokens, *token)
|
||||
if pos < 0 {
|
||||
return
|
||||
}
|
||||
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
|
||||
parser.tokens[parser.tokens_head+pos] = *token
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Check if the character at the specified position is BOM.
|
||||
// */
|
||||
//
|
||||
func is_bom_at(b []byte, i int) bool {
|
||||
return b[i] == 0xEF && b[i+1] == 0xBB && b[i+2] == 0xBF
|
||||
}
|
||||
|
||||
//
|
||||
// #ifdef HAVE_CONFIG_H
|
||||
// #include <config.h>
|
||||
// #endif
|
||||
//
|
||||
// #include "./yaml.h"
|
||||
//
|
||||
// #include <assert.h>
|
||||
// #include <limits.h>
|
||||
//
|
||||
// /*
|
||||
// * Memory management.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(void *)
|
||||
// yaml_malloc(size_t size);
|
||||
//
|
||||
// yaml_DECLARE(void *)
|
||||
// yaml_realloc(void *ptr, size_t size);
|
||||
//
|
||||
// yaml_DECLARE(void)
|
||||
// yaml_free(void *ptr);
|
||||
//
|
||||
// yaml_DECLARE(yaml_char_t *)
|
||||
// yaml_strdup(const yaml_char_t *);
|
||||
//
|
||||
// /*
|
||||
// * Reader: Ensure that the buffer contains at least `length` characters.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_parser_update_buffer(yaml_parser_t *parser, size_t length);
|
||||
//
|
||||
// /*
|
||||
// * Scanner: Ensure that the token stack contains at least one token ready.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_parser_fetch_more_tokens(yaml_parser_t *parser);
|
||||
//
|
||||
// /*
|
||||
// * The size of the input raw buffer.
|
||||
// */
|
||||
//
|
||||
// #define INPUT_RAW_BUFFER_SIZE 16384
|
||||
//
|
||||
// /*
|
||||
// * The size of the input buffer.
|
||||
// *
|
||||
// * It should be possible to decode the whole raw buffer.
|
||||
// */
|
||||
//
|
||||
// #define INPUT_BUFFER_SIZE (INPUT_RAW_BUFFER_SIZE*3)
|
||||
//
|
||||
// /*
|
||||
// * The size of the output buffer.
|
||||
// */
|
||||
//
|
||||
// #define OUTPUT_BUFFER_SIZE 16384
|
||||
//
|
||||
// /*
|
||||
// * The size of the output raw buffer.
|
||||
// *
|
||||
// * It should be possible to encode the whole output buffer.
|
||||
// */
|
||||
//
|
||||
// #define OUTPUT_RAW_BUFFER_SIZE (OUTPUT_BUFFER_SIZE*2+2)
|
||||
//
|
||||
// /*
|
||||
// * The size of other stacks and queues.
|
||||
// */
|
||||
//
|
||||
// #define INITIAL_STACK_SIZE 16
|
||||
// #define INITIAL_QUEUE_SIZE 16
|
||||
// #define INITIAL_STRING_SIZE 16
|
||||
//
|
||||
// /*
|
||||
// * Buffer management.
|
||||
// */
|
||||
//
|
||||
// #define BUFFER_INIT(context,buffer,size) \
|
||||
// (((buffer).start = yaml_malloc(size)) ? \
|
||||
// ((buffer).last = (buffer).pointer = (buffer).start, \
|
||||
// (buffer).end = (buffer).start+(size), \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define BUFFER_DEL(context,buffer) \
|
||||
// (yaml_free((buffer).start), \
|
||||
// (buffer).start = (buffer).pointer = (buffer).end = 0)
|
||||
//
|
||||
// /*
|
||||
// * String management.
|
||||
// */
|
||||
//
|
||||
// typedef struct {
|
||||
// yaml_char_t *start;
|
||||
// yaml_char_t *end;
|
||||
// yaml_char_t *pointer;
|
||||
// } yaml_string_t;
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_string_extend(yaml_char_t **start,
|
||||
// yaml_char_t **pointer, yaml_char_t **end);
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_string_join(
|
||||
// yaml_char_t **a_start, yaml_char_t **a_pointer, yaml_char_t **a_end,
|
||||
// yaml_char_t **b_start, yaml_char_t **b_pointer, yaml_char_t **b_end);
|
||||
//
|
||||
// #define NULL_STRING { NULL, NULL, NULL }
|
||||
//
|
||||
// #define STRING(string,length) { (string), (string)+(length), (string) }
|
||||
//
|
||||
// #define STRING_ASSIGN(value,string,length) \
|
||||
// ((value).start = (string), \
|
||||
// (value).end = (string)+(length), \
|
||||
// (value).pointer = (string))
|
||||
//
|
||||
// #define STRING_INIT(context,string,size) \
|
||||
// (((string).start = yaml_malloc(size)) ? \
|
||||
// ((string).pointer = (string).start, \
|
||||
// (string).end = (string).start+(size), \
|
||||
// memset((string).start, 0, (size)), \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define STRING_DEL(context,string) \
|
||||
// (yaml_free((string).start), \
|
||||
// (string).start = (string).pointer = (string).end = 0)
|
||||
//
|
||||
// #define STRING_EXTEND(context,string) \
|
||||
// (((string).pointer+5 < (string).end) \
|
||||
// || yaml_string_extend(&(string).start, \
|
||||
// &(string).pointer, &(string).end))
|
||||
//
|
||||
// #define CLEAR(context,string) \
|
||||
// ((string).pointer = (string).start, \
|
||||
// memset((string).start, 0, (string).end-(string).start))
|
||||
//
|
||||
// #define JOIN(context,string_a,string_b) \
|
||||
// ((yaml_string_join(&(string_a).start, &(string_a).pointer, \
|
||||
// &(string_a).end, &(string_b).start, \
|
||||
// &(string_b).pointer, &(string_b).end)) ? \
|
||||
// ((string_b).pointer = (string_b).start, \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// /*
|
||||
// * String check operations.
|
||||
// */
|
||||
//
|
||||
// /*
|
||||
// * Check the octet at the specified position.
|
||||
// */
|
||||
//
|
||||
// #define CHECK_AT(string,octet,offset) \
|
||||
// ((string).pointer[offset] == (yaml_char_t)(octet))
|
||||
//
|
||||
// /*
|
||||
// * Check the current octet in the buffer.
|
||||
// */
|
||||
//
|
||||
// #define CHECK(string,octet) CHECK_AT((string),(octet),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is an alphabetical
|
||||
// * character, a digit, '_', or '-'.
|
||||
// */
|
||||
//
|
||||
// #define IS_ALPHA_AT(string,offset) \
|
||||
// (((string).pointer[offset] >= (yaml_char_t) '0' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) '9') || \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'A' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'Z') || \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'a' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'z') || \
|
||||
// (string).pointer[offset] == '_' || \
|
||||
// (string).pointer[offset] == '-')
|
||||
//
|
||||
// #define IS_ALPHA(string) IS_ALPHA_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is a digit.
|
||||
// */
|
||||
//
|
||||
// #define IS_DIGIT_AT(string,offset) \
|
||||
// (((string).pointer[offset] >= (yaml_char_t) '0' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) '9'))
|
||||
//
|
||||
// #define IS_DIGIT(string) IS_DIGIT_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Get the value of a digit.
|
||||
// */
|
||||
//
|
||||
// #define AS_DIGIT_AT(string,offset) \
|
||||
// ((string).pointer[offset] - (yaml_char_t) '0')
|
||||
//
|
||||
// #define AS_DIGIT(string) AS_DIGIT_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is a hex-digit.
|
||||
// */
|
||||
//
|
||||
// #define IS_HEX_AT(string,offset) \
|
||||
// (((string).pointer[offset] >= (yaml_char_t) '0' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) '9') || \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'A' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'F') || \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'a' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'f'))
|
||||
//
|
||||
// #define IS_HEX(string) IS_HEX_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Get the value of a hex-digit.
|
||||
// */
|
||||
//
|
||||
// #define AS_HEX_AT(string,offset) \
|
||||
// (((string).pointer[offset] >= (yaml_char_t) 'A' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'F') ? \
|
||||
// ((string).pointer[offset] - (yaml_char_t) 'A' + 10) : \
|
||||
// ((string).pointer[offset] >= (yaml_char_t) 'a' && \
|
||||
// (string).pointer[offset] <= (yaml_char_t) 'f') ? \
|
||||
// ((string).pointer[offset] - (yaml_char_t) 'a' + 10) : \
|
||||
// ((string).pointer[offset] - (yaml_char_t) '0'))
|
||||
//
|
||||
// #define AS_HEX(string) AS_HEX_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character is ASCII.
|
||||
// */
|
||||
//
|
||||
// #define IS_ASCII_AT(string,offset) \
|
||||
// ((string).pointer[offset] <= (yaml_char_t) '\x7F')
|
||||
//
|
||||
// #define IS_ASCII(string) IS_ASCII_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character can be printed unescaped.
|
||||
// */
|
||||
//
|
||||
// #define IS_PRINTABLE_AT(string,offset) \
|
||||
// (((string).pointer[offset] == 0x0A) /* . == #x0A */ \
|
||||
// || ((string).pointer[offset] >= 0x20 /* #x20 <= . <= #x7E */ \
|
||||
// && (string).pointer[offset] <= 0x7E) \
|
||||
// || ((string).pointer[offset] == 0xC2 /* #0xA0 <= . <= #xD7FF */ \
|
||||
// && (string).pointer[offset+1] >= 0xA0) \
|
||||
// || ((string).pointer[offset] > 0xC2 \
|
||||
// && (string).pointer[offset] < 0xED) \
|
||||
// || ((string).pointer[offset] == 0xED \
|
||||
// && (string).pointer[offset+1] < 0xA0) \
|
||||
// || ((string).pointer[offset] == 0xEE) \
|
||||
// || ((string).pointer[offset] == 0xEF /* #xE000 <= . <= #xFFFD */ \
|
||||
// && !((string).pointer[offset+1] == 0xBB /* && . != #xFEFF */ \
|
||||
// && (string).pointer[offset+2] == 0xBF) \
|
||||
// && !((string).pointer[offset+1] == 0xBF \
|
||||
// && ((string).pointer[offset+2] == 0xBE \
|
||||
// || (string).pointer[offset+2] == 0xBF))))
|
||||
//
|
||||
// #define IS_PRINTABLE(string) IS_PRINTABLE_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is NUL.
|
||||
// */
|
||||
//
|
||||
// #define IS_Z_AT(string,offset) CHECK_AT((string),'\0',(offset))
|
||||
//
|
||||
// #define IS_Z(string) IS_Z_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is BOM.
|
||||
// */
|
||||
//
|
||||
// #define IS_BOM_AT(string,offset) \
|
||||
// (CHECK_AT((string),'\xEF',(offset)) \
|
||||
// && CHECK_AT((string),'\xBB',(offset)+1) \
|
||||
// && CHECK_AT((string),'\xBF',(offset)+2)) /* BOM (#xFEFF) */
|
||||
//
|
||||
// #define IS_BOM(string) IS_BOM_AT(string,0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is space.
|
||||
// */
|
||||
//
|
||||
// #define IS_SPACE_AT(string,offset) CHECK_AT((string),' ',(offset))
|
||||
//
|
||||
// #define IS_SPACE(string) IS_SPACE_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is tab.
|
||||
// */
|
||||
//
|
||||
// #define IS_TAB_AT(string,offset) CHECK_AT((string),'\t',(offset))
|
||||
//
|
||||
// #define IS_TAB(string) IS_TAB_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is blank (space or tab).
|
||||
// */
|
||||
//
|
||||
// #define IS_BLANK_AT(string,offset) \
|
||||
// (IS_SPACE_AT((string),(offset)) || IS_TAB_AT((string),(offset)))
|
||||
//
|
||||
// #define IS_BLANK(string) IS_BLANK_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character at the specified position is a line break.
|
||||
// */
|
||||
//
|
||||
// #define IS_BREAK_AT(string,offset) \
|
||||
// (CHECK_AT((string),'\r',(offset)) /* CR (#xD)*/ \
|
||||
// || CHECK_AT((string),'\n',(offset)) /* LF (#xA) */ \
|
||||
// || (CHECK_AT((string),'\xC2',(offset)) \
|
||||
// && CHECK_AT((string),'\x85',(offset)+1)) /* NEL (#x85) */ \
|
||||
// || (CHECK_AT((string),'\xE2',(offset)) \
|
||||
// && CHECK_AT((string),'\x80',(offset)+1) \
|
||||
// && CHECK_AT((string),'\xA8',(offset)+2)) /* LS (#x2028) */ \
|
||||
// || (CHECK_AT((string),'\xE2',(offset)) \
|
||||
// && CHECK_AT((string),'\x80',(offset)+1) \
|
||||
// && CHECK_AT((string),'\xA9',(offset)+2))) /* PS (#x2029) */
|
||||
//
|
||||
// #define IS_BREAK(string) IS_BREAK_AT((string),0)
|
||||
//
|
||||
// #define IS_CRLF_AT(string,offset) \
|
||||
// (CHECK_AT((string),'\r',(offset)) && CHECK_AT((string),'\n',(offset)+1))
|
||||
//
|
||||
// #define IS_CRLF(string) IS_CRLF_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character is a line break or NUL.
|
||||
// */
|
||||
//
|
||||
// #define IS_BREAKZ_AT(string,offset) \
|
||||
// (IS_BREAK_AT((string),(offset)) || IS_Z_AT((string),(offset)))
|
||||
//
|
||||
// #define IS_BREAKZ(string) IS_BREAKZ_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character is a line break, space, or NUL.
|
||||
// */
|
||||
//
|
||||
// #define IS_SPACEZ_AT(string,offset) \
|
||||
// (IS_SPACE_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset)))
|
||||
//
|
||||
// #define IS_SPACEZ(string) IS_SPACEZ_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Check if the character is a line break, space, tab, or NUL.
|
||||
// */
|
||||
//
|
||||
// #define IS_BLANKZ_AT(string,offset) \
|
||||
// (IS_BLANK_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset)))
|
||||
//
|
||||
// #define IS_BLANKZ(string) IS_BLANKZ_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Determine the width of the character.
|
||||
// */
|
||||
//
|
||||
// #define WIDTH_AT(string,offset) \
|
||||
// (((string).pointer[offset] & 0x80) == 0x00 ? 1 : \
|
||||
// ((string).pointer[offset] & 0xE0) == 0xC0 ? 2 : \
|
||||
// ((string).pointer[offset] & 0xF0) == 0xE0 ? 3 : \
|
||||
// ((string).pointer[offset] & 0xF8) == 0xF0 ? 4 : 0)
|
||||
//
|
||||
// #define WIDTH(string) WIDTH_AT((string),0)
|
||||
//
|
||||
// /*
|
||||
// * Move the string pointer to the next character.
|
||||
// */
|
||||
//
|
||||
// #define MOVE(string) ((string).pointer += WIDTH((string)))
|
||||
//
|
||||
// /*
|
||||
// * Copy a character and move the pointers of both strings.
|
||||
// */
|
||||
//
|
||||
// #define COPY(string_a,string_b) \
|
||||
// ((*(string_b).pointer & 0x80) == 0x00 ? \
|
||||
// (*((string_a).pointer++) = *((string_b).pointer++)) : \
|
||||
// (*(string_b).pointer & 0xE0) == 0xC0 ? \
|
||||
// (*((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++)) : \
|
||||
// (*(string_b).pointer & 0xF0) == 0xE0 ? \
|
||||
// (*((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++)) : \
|
||||
// (*(string_b).pointer & 0xF8) == 0xF0 ? \
|
||||
// (*((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++), \
|
||||
// *((string_a).pointer++) = *((string_b).pointer++)) : 0)
|
||||
//
|
||||
// /*
|
||||
// * Stack and queue management.
|
||||
// */
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_stack_extend(void **start, void **top, void **end);
|
||||
//
|
||||
// yaml_DECLARE(int)
|
||||
// yaml_queue_extend(void **start, void **head, void **tail, void **end);
|
||||
//
|
||||
// #define STACK_INIT(context,stack,size) \
|
||||
// (((stack).start = yaml_malloc((size)*sizeof(*(stack).start))) ? \
|
||||
// ((stack).top = (stack).start, \
|
||||
// (stack).end = (stack).start+(size), \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define STACK_DEL(context,stack) \
|
||||
// (yaml_free((stack).start), \
|
||||
// (stack).start = (stack).top = (stack).end = 0)
|
||||
//
|
||||
// #define STACK_EMPTY(context,stack) \
|
||||
// ((stack).start == (stack).top)
|
||||
//
|
||||
// #define PUSH(context,stack,value) \
|
||||
// (((stack).top != (stack).end \
|
||||
// || yaml_stack_extend((void **)&(stack).start, \
|
||||
// (void **)&(stack).top, (void **)&(stack).end)) ? \
|
||||
// (*((stack).top++) = value, \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define POP(context,stack) \
|
||||
// (*(--(stack).top))
|
||||
//
|
||||
// #define QUEUE_INIT(context,queue,size) \
|
||||
// (((queue).start = yaml_malloc((size)*sizeof(*(queue).start))) ? \
|
||||
// ((queue).head = (queue).tail = (queue).start, \
|
||||
// (queue).end = (queue).start+(size), \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define QUEUE_DEL(context,queue) \
|
||||
// (yaml_free((queue).start), \
|
||||
// (queue).start = (queue).head = (queue).tail = (queue).end = 0)
|
||||
//
|
||||
// #define QUEUE_EMPTY(context,queue) \
|
||||
// ((queue).head == (queue).tail)
|
||||
//
|
||||
// #define ENQUEUE(context,queue,value) \
|
||||
// (((queue).tail != (queue).end \
|
||||
// || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \
|
||||
// (void **)&(queue).tail, (void **)&(queue).end)) ? \
|
||||
// (*((queue).tail++) = value, \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// #define DEQUEUE(context,queue) \
|
||||
// (*((queue).head++))
|
||||
//
|
||||
// #define QUEUE_INSERT(context,queue,index,value) \
|
||||
// (((queue).tail != (queue).end \
|
||||
// || yaml_queue_extend((void **)&(queue).start, (void **)&(queue).head, \
|
||||
// (void **)&(queue).tail, (void **)&(queue).end)) ? \
|
||||
// (memmove((queue).head+(index)+1,(queue).head+(index), \
|
||||
// ((queue).tail-(queue).head-(index))*sizeof(*(queue).start)), \
|
||||
// *((queue).head+(index)) = value, \
|
||||
// (queue).tail++, \
|
||||
// 1) : \
|
||||
// ((context)->error = yaml_MEMORY_ERROR, \
|
||||
// 0))
|
||||
//
|
||||
// /*
|
||||
// * Token initializers.
|
||||
// */
|
||||
//
|
||||
// #define TOKEN_INIT(token,token_type,token_start_mark,token_end_mark) \
|
||||
// (memset(&(token), 0, sizeof(yaml_token_t)), \
|
||||
// (token).type = (token_type), \
|
||||
// (token).start_mark = (token_start_mark), \
|
||||
// (token).end_mark = (token_end_mark))
|
||||
//
|
||||
// #define STREAM_START_TOKEN_INIT(token,token_encoding,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_STREAM_START_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.stream_start.encoding = (token_encoding))
|
||||
//
|
||||
// #define STREAM_END_TOKEN_INIT(token,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_STREAM_END_TOKEN,(start_mark),(end_mark)))
|
||||
//
|
||||
// #define ALIAS_TOKEN_INIT(token,token_value,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_ALIAS_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.alias.value = (token_value))
|
||||
//
|
||||
// #define ANCHOR_TOKEN_INIT(token,token_value,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_ANCHOR_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.anchor.value = (token_value))
|
||||
//
|
||||
// #define TAG_TOKEN_INIT(token,token_handle,token_suffix,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_TAG_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.tag.handle = (token_handle), \
|
||||
// (token).data.tag.suffix = (token_suffix))
|
||||
//
|
||||
// #define SCALAR_TOKEN_INIT(token,token_value,token_length,token_style,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_SCALAR_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.scalar.value = (token_value), \
|
||||
// (token).data.scalar.length = (token_length), \
|
||||
// (token).data.scalar.style = (token_style))
|
||||
//
|
||||
// #define VERSION_DIRECTIVE_TOKEN_INIT(token,token_major,token_minor,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_VERSION_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.version_directive.major = (token_major), \
|
||||
// (token).data.version_directive.minor = (token_minor))
|
||||
//
|
||||
// #define TAG_DIRECTIVE_TOKEN_INIT(token,token_handle,token_prefix,start_mark,end_mark) \
|
||||
// (TOKEN_INIT((token),yaml_TAG_DIRECTIVE_TOKEN,(start_mark),(end_mark)), \
|
||||
// (token).data.tag_directive.handle = (token_handle), \
|
||||
// (token).data.tag_directive.prefix = (token_prefix))
|
||||
//
|
||||
// /*
|
||||
// * Event initializers.
|
||||
// */
|
||||
//
|
||||
// #define EVENT_INIT(event,event_type,event_start_mark,event_end_mark) \
|
||||
// (memset(&(event), 0, sizeof(yaml_event_t)), \
|
||||
// (event).type = (event_type), \
|
||||
// (event).start_mark = (event_start_mark), \
|
||||
// (event).end_mark = (event_end_mark))
|
||||
//
|
||||
// #define STREAM_START_EVENT_INIT(event,event_encoding,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_STREAM_START_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.stream_start.encoding = (event_encoding))
|
||||
//
|
||||
// #define STREAM_END_EVENT_INIT(event,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_STREAM_END_EVENT,(start_mark),(end_mark)))
|
||||
//
|
||||
// #define DOCUMENT_START_EVENT_INIT(event,event_version_directive, \
|
||||
// event_tag_directives_start,event_tag_directives_end,event_implicit,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_DOCUMENT_START_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.document_start.version_directive = (event_version_directive), \
|
||||
// (event).data.document_start.tag_directives.start = (event_tag_directives_start), \
|
||||
// (event).data.document_start.tag_directives.end = (event_tag_directives_end), \
|
||||
// (event).data.document_start.implicit = (event_implicit))
|
||||
//
|
||||
// #define DOCUMENT_END_EVENT_INIT(event,event_implicit,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_DOCUMENT_END_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.document_end.implicit = (event_implicit))
|
||||
//
|
||||
// #define ALIAS_EVENT_INIT(event,event_anchor,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_ALIAS_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.alias.anchor = (event_anchor))
|
||||
//
|
||||
// #define SCALAR_EVENT_INIT(event,event_anchor,event_tag,event_value,event_length, \
|
||||
// event_plain_implicit, event_quoted_implicit,event_style,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_SCALAR_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.scalar.anchor = (event_anchor), \
|
||||
// (event).data.scalar.tag = (event_tag), \
|
||||
// (event).data.scalar.value = (event_value), \
|
||||
// (event).data.scalar.length = (event_length), \
|
||||
// (event).data.scalar.plain_implicit = (event_plain_implicit), \
|
||||
// (event).data.scalar.quoted_implicit = (event_quoted_implicit), \
|
||||
// (event).data.scalar.style = (event_style))
|
||||
//
|
||||
// #define SEQUENCE_START_EVENT_INIT(event,event_anchor,event_tag, \
|
||||
// event_implicit,event_style,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_SEQUENCE_START_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.sequence_start.anchor = (event_anchor), \
|
||||
// (event).data.sequence_start.tag = (event_tag), \
|
||||
// (event).data.sequence_start.implicit = (event_implicit), \
|
||||
// (event).data.sequence_start.style = (event_style))
|
||||
//
|
||||
// #define SEQUENCE_END_EVENT_INIT(event,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_SEQUENCE_END_EVENT,(start_mark),(end_mark)))
|
||||
//
|
||||
// #define MAPPING_START_EVENT_INIT(event,event_anchor,event_tag, \
|
||||
// event_implicit,event_style,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_MAPPING_START_EVENT,(start_mark),(end_mark)), \
|
||||
// (event).data.mapping_start.anchor = (event_anchor), \
|
||||
// (event).data.mapping_start.tag = (event_tag), \
|
||||
// (event).data.mapping_start.implicit = (event_implicit), \
|
||||
// (event).data.mapping_start.style = (event_style))
|
||||
//
|
||||
// #define MAPPING_END_EVENT_INIT(event,start_mark,end_mark) \
|
||||
// (EVENT_INIT((event),yaml_MAPPING_END_EVENT,(start_mark),(end_mark)))
|
||||
//
|
||||
// /*
|
||||
// * Document initializer.
|
||||
// */
|
||||
//
|
||||
// #define DOCUMENT_INIT(document,document_nodes_start,document_nodes_end, \
|
||||
// document_version_directive,document_tag_directives_start, \
|
||||
// document_tag_directives_end,document_start_implicit, \
|
||||
// document_end_implicit,document_start_mark,document_end_mark) \
|
||||
// (memset(&(document), 0, sizeof(yaml_document_t)), \
|
||||
// (document).nodes.start = (document_nodes_start), \
|
||||
// (document).nodes.end = (document_nodes_end), \
|
||||
// (document).nodes.top = (document_nodes_start), \
|
||||
// (document).version_directive = (document_version_directive), \
|
||||
// (document).tag_directives.start = (document_tag_directives_start), \
|
||||
// (document).tag_directives.end = (document_tag_directives_end), \
|
||||
// (document).start_implicit = (document_start_implicit), \
|
||||
// (document).end_implicit = (document_end_implicit), \
|
||||
// (document).start_mark = (document_start_mark), \
|
||||
// (document).end_mark = (document_end_mark))
|
||||
//
|
||||
// /*
|
||||
// * Node initializers.
|
||||
// */
|
||||
//
|
||||
// #define NODE_INIT(node,node_type,node_tag,node_start_mark,node_end_mark) \
|
||||
// (memset(&(node), 0, sizeof(yaml_node_t)), \
|
||||
// (node).type = (node_type), \
|
||||
// (node).tag = (node_tag), \
|
||||
// (node).start_mark = (node_start_mark), \
|
||||
// (node).end_mark = (node_end_mark))
|
||||
//
|
||||
// #define SCALAR_NODE_INIT(node,node_tag,node_value,node_length, \
|
||||
// node_style,start_mark,end_mark) \
|
||||
// (NODE_INIT((node),yaml_SCALAR_NODE,(node_tag),(start_mark),(end_mark)), \
|
||||
// (node).data.scalar.value = (node_value), \
|
||||
// (node).data.scalar.length = (node_length), \
|
||||
// (node).data.scalar.style = (node_style))
|
||||
//
|
||||
// #define SEQUENCE_NODE_INIT(node,node_tag,node_items_start,node_items_end, \
|
||||
// node_style,start_mark,end_mark) \
|
||||
// (NODE_INIT((node),yaml_SEQUENCE_NODE,(node_tag),(start_mark),(end_mark)), \
|
||||
// (node).data.sequence.items.start = (node_items_start), \
|
||||
// (node).data.sequence.items.end = (node_items_end), \
|
||||
// (node).data.sequence.items.top = (node_items_start), \
|
||||
// (node).data.sequence.style = (node_style))
|
||||
//
|
||||
// #define MAPPING_NODE_INIT(node,node_tag,node_pairs_start,node_pairs_end, \
|
||||
// node_style,start_mark,end_mark) \
|
||||
// (NODE_INIT((node),yaml_MAPPING_NODE,(node_tag),(start_mark),(end_mark)), \
|
||||
// (node).data.mapping.pairs.start = (node_pairs_start), \
|
||||
// (node).data.mapping.pairs.end = (node_pairs_end), \
|
||||
// (node).data.mapping.pairs.top = (node_pairs_start), \
|
||||
// (node).data.mapping.style = (node_style))
|
||||
//
|
|
@ -1,953 +0,0 @@
|
|||
/*
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package candiedyaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
/** The version directive data. */
|
||||
type yaml_version_directive_t struct {
|
||||
major int // The major version number
|
||||
minor int // The minor version number
|
||||
}
|
||||
|
||||
/** The tag directive data. */
|
||||
type yaml_tag_directive_t struct {
|
||||
handle []byte // The tag handle
|
||||
prefix []byte // The tag prefix
|
||||
}
|
||||
|
||||
/** The stream encoding. */
|
||||
type yaml_encoding_t int
|
||||
|
||||
const (
|
||||
/** Let the parser choose the encoding. */
|
||||
yaml_ANY_ENCODING yaml_encoding_t = iota
|
||||
/** The defau lt UTF-8 encoding. */
|
||||
yaml_UTF8_ENCODING
|
||||
/** The UTF-16-LE encoding with BOM. */
|
||||
yaml_UTF16LE_ENCODING
|
||||
/** The UTF-16-BE encoding with BOM. */
|
||||
yaml_UTF16BE_ENCODING
|
||||
)
|
||||
|
||||
/** Line break types. */
|
||||
type yaml_break_t int
|
||||
|
||||
const (
|
||||
yaml_ANY_BREAK yaml_break_t = iota /** Let the parser choose the break type. */
|
||||
yaml_CR_BREAK /** Use CR for line breaks (Mac style). */
|
||||
yaml_LN_BREAK /** Use LN for line breaks (Unix style). */
|
||||
yaml_CRLN_BREAK /** Use CR LN for line breaks (DOS style). */
|
||||
)
|
||||
|
||||
/** Many bad things could happen with the parser and emitter. */
|
||||
type YAML_error_type_t int
|
||||
|
||||
const (
|
||||
/** No error is produced. */
|
||||
yaml_NO_ERROR YAML_error_type_t = iota
|
||||
|
||||
/** Cannot allocate or reallocate a block of memory. */
|
||||
yaml_MEMORY_ERROR
|
||||
|
||||
/** Cannot read or decode the input stream. */
|
||||
yaml_READER_ERROR
|
||||
/** Cannot scan the input stream. */
|
||||
yaml_SCANNER_ERROR
|
||||
/** Cannot parse the input stream. */
|
||||
yaml_PARSER_ERROR
|
||||
/** Cannot compose a YAML document. */
|
||||
yaml_COMPOSER_ERROR
|
||||
|
||||
/** Cannot write to the output stream. */
|
||||
yaml_WRITER_ERROR
|
||||
/** Cannot emit a YAML stream. */
|
||||
yaml_EMITTER_ERROR
|
||||
)
|
||||
|
||||
/** The pointer position. */
|
||||
type YAML_mark_t struct {
|
||||
/** The position index. */
|
||||
index int
|
||||
|
||||
/** The position line. */
|
||||
line int
|
||||
|
||||
/** The position column. */
|
||||
column int
|
||||
}
|
||||
|
||||
func (m YAML_mark_t) String() string {
|
||||
return fmt.Sprintf("line %d, column %d", m.line, m.column)
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup styles Node Styles
|
||||
* @{
|
||||
*/
|
||||
|
||||
type yaml_style_t int
|
||||
|
||||
/** Scalar styles. */
|
||||
type yaml_scalar_style_t yaml_style_t
|
||||
|
||||
const (
|
||||
/** Let the emitter choose the style. */
|
||||
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
|
||||
|
||||
/** The plain scalar style. */
|
||||
yaml_PLAIN_SCALAR_STYLE
|
||||
|
||||
/** The single-quoted scalar style. */
|
||||
yaml_SINGLE_QUOTED_SCALAR_STYLE
|
||||
/** The double-quoted scalar style. */
|
||||
yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
|
||||
/** The literal scalar style. */
|
||||
yaml_LITERAL_SCALAR_STYLE
|
||||
/** The folded scalar style. */
|
||||
yaml_FOLDED_SCALAR_STYLE
|
||||
)
|
||||
|
||||
/** Sequence styles. */
|
||||
type yaml_sequence_style_t yaml_style_t
|
||||
|
||||
const (
|
||||
/** Let the emitter choose the style. */
|
||||
yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
|
||||
|
||||
/** The block sequence style. */
|
||||
yaml_BLOCK_SEQUENCE_STYLE
|
||||
/** The flow sequence style. */
|
||||
yaml_FLOW_SEQUENCE_STYLE
|
||||
)
|
||||
|
||||
/** Mapping styles. */
|
||||
type yaml_mapping_style_t yaml_style_t
|
||||
|
||||
const (
|
||||
/** Let the emitter choose the style. */
|
||||
yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
|
||||
|
||||
/** The block mapping style. */
|
||||
yaml_BLOCK_MAPPING_STYLE
|
||||
/** The flow mapping style. */
|
||||
yaml_FLOW_MAPPING_STYLE
|
||||
|
||||
/* yaml_FLOW_SET_MAPPING_STYLE */
|
||||
)
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup tokens Tokens
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Token types. */
|
||||
type yaml_token_type_t int
|
||||
|
||||
const (
|
||||
/** An empty token. */
|
||||
yaml_NO_TOKEN yaml_token_type_t = iota
|
||||
|
||||
/** A STREAM-START token. */
|
||||
yaml_STREAM_START_TOKEN
|
||||
/** A STREAM-END token. */
|
||||
yaml_STREAM_END_TOKEN
|
||||
|
||||
/** A VERSION-DIRECTIVE token. */
|
||||
yaml_VERSION_DIRECTIVE_TOKEN
|
||||
/** A TAG-DIRECTIVE token. */
|
||||
yaml_TAG_DIRECTIVE_TOKEN
|
||||
/** A DOCUMENT-START token. */
|
||||
yaml_DOCUMENT_START_TOKEN
|
||||
/** A DOCUMENT-END token. */
|
||||
yaml_DOCUMENT_END_TOKEN
|
||||
|
||||
/** A BLOCK-SEQUENCE-START token. */
|
||||
yaml_BLOCK_SEQUENCE_START_TOKEN
|
||||
/** A BLOCK-SEQUENCE-END token. */
|
||||
yaml_BLOCK_MAPPING_START_TOKEN
|
||||
/** A BLOCK-END token. */
|
||||
yaml_BLOCK_END_TOKEN
|
||||
|
||||
/** A FLOW-SEQUENCE-START token. */
|
||||
yaml_FLOW_SEQUENCE_START_TOKEN
|
||||
/** A FLOW-SEQUENCE-END token. */
|
||||
yaml_FLOW_SEQUENCE_END_TOKEN
|
||||
/** A FLOW-MAPPING-START token. */
|
||||
yaml_FLOW_MAPPING_START_TOKEN
|
||||
/** A FLOW-MAPPING-END token. */
|
||||
yaml_FLOW_MAPPING_END_TOKEN
|
||||
|
||||
/** A BLOCK-ENTRY token. */
|
||||
yaml_BLOCK_ENTRY_TOKEN
|
||||
/** A FLOW-ENTRY token. */
|
||||
yaml_FLOW_ENTRY_TOKEN
|
||||
/** A KEY token. */
|
||||
yaml_KEY_TOKEN
|
||||
/** A VALUE token. */
|
||||
yaml_VALUE_TOKEN
|
||||
|
||||
/** An ALIAS token. */
|
||||
yaml_ALIAS_TOKEN
|
||||
/** An ANCHOR token. */
|
||||
yaml_ANCHOR_TOKEN
|
||||
/** A TAG token. */
|
||||
yaml_TAG_TOKEN
|
||||
/** A SCALAR token. */
|
||||
yaml_SCALAR_TOKEN
|
||||
)
|
||||
|
||||
/** The token structure. */
|
||||
type yaml_token_t struct {
|
||||
|
||||
/** The token type. */
|
||||
token_type yaml_token_type_t
|
||||
|
||||
/** The token data. */
|
||||
/** The stream start (for @c yaml_STREAM_START_TOKEN). */
|
||||
encoding yaml_encoding_t
|
||||
|
||||
/** The alias (for @c yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN,yaml_TAG_TOKEN ). */
|
||||
/** The anchor (for @c ). */
|
||||
/** The scalar value (for @c ). */
|
||||
value []byte
|
||||
|
||||
/** The tag suffix. */
|
||||
suffix []byte
|
||||
|
||||
/** The scalar value (for @c yaml_SCALAR_TOKEN). */
|
||||
/** The scalar style. */
|
||||
style yaml_scalar_style_t
|
||||
|
||||
/** The version directive (for @c yaml_VERSION_DIRECTIVE_TOKEN). */
|
||||
version_directive yaml_version_directive_t
|
||||
|
||||
/** The tag directive (for @c yaml_TAG_DIRECTIVE_TOKEN). */
|
||||
prefix []byte
|
||||
|
||||
/** The beginning of the token. */
|
||||
start_mark YAML_mark_t
|
||||
/** The end of the token. */
|
||||
end_mark YAML_mark_t
|
||||
|
||||
major, minor int
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup events Events
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Event types. */
|
||||
type yaml_event_type_t int
|
||||
|
||||
const (
|
||||
/** An empty event. */
|
||||
yaml_NO_EVENT yaml_event_type_t = iota
|
||||
|
||||
/** A STREAM-START event. */
|
||||
yaml_STREAM_START_EVENT
|
||||
/** A STREAM-END event. */
|
||||
yaml_STREAM_END_EVENT
|
||||
|
||||
/** A DOCUMENT-START event. */
|
||||
yaml_DOCUMENT_START_EVENT
|
||||
/** A DOCUMENT-END event. */
|
||||
yaml_DOCUMENT_END_EVENT
|
||||
|
||||
/** An ALIAS event. */
|
||||
yaml_ALIAS_EVENT
|
||||
/** A SCALAR event. */
|
||||
yaml_SCALAR_EVENT
|
||||
|
||||
/** A SEQUENCE-START event. */
|
||||
yaml_SEQUENCE_START_EVENT
|
||||
/** A SEQUENCE-END event. */
|
||||
yaml_SEQUENCE_END_EVENT
|
||||
|
||||
/** A MAPPING-START event. */
|
||||
yaml_MAPPING_START_EVENT
|
||||
/** A MAPPING-END event. */
|
||||
yaml_MAPPING_END_EVENT
|
||||
)
|
||||
|
||||
/** The event structure. */
|
||||
type yaml_event_t struct {
|
||||
|
||||
/** The event type. */
|
||||
event_type yaml_event_type_t
|
||||
|
||||
/** The stream parameters (for @c yaml_STREAM_START_EVENT). */
|
||||
encoding yaml_encoding_t
|
||||
|
||||
/** The document parameters (for @c yaml_DOCUMENT_START_EVENT). */
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
/** The beginning and end of the tag directives list. */
|
||||
tag_directives []yaml_tag_directive_t
|
||||
|
||||
/** The document parameters (for @c yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT,yaml_MAPPING_START_EVENT). */
|
||||
/** Is the document indicator implicit? */
|
||||
implicit bool
|
||||
|
||||
/** The alias parameters (for @c yaml_ALIAS_EVENT,yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). */
|
||||
/** The anchor. */
|
||||
anchor []byte
|
||||
|
||||
/** The scalar parameters (for @c yaml_SCALAR_EVENT,yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). */
|
||||
/** The tag. */
|
||||
tag []byte
|
||||
/** The scalar value. */
|
||||
value []byte
|
||||
|
||||
/** Is the tag optional for the plain style? */
|
||||
plain_implicit bool
|
||||
/** Is the tag optional for any non-plain style? */
|
||||
quoted_implicit bool
|
||||
|
||||
/** The sequence parameters (for @c yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). */
|
||||
/** The sequence style. */
|
||||
/** The scalar style. */
|
||||
style yaml_style_t
|
||||
|
||||
/** The beginning of the event. */
|
||||
start_mark, end_mark YAML_mark_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup nodes Nodes
|
||||
* @{
|
||||
*/
|
||||
|
||||
const (
|
||||
/** The tag @c !!null with the only possible value: @c null. */
|
||||
yaml_NULL_TAG = "tag:yaml.org,2002:null"
|
||||
/** The tag @c !!bool with the values: @c true and @c falce. */
|
||||
yaml_BOOL_TAG = "tag:yaml.org,2002:bool"
|
||||
/** The tag @c !!str for string values. */
|
||||
yaml_STR_TAG = "tag:yaml.org,2002:str"
|
||||
/** The tag @c !!int for integer values. */
|
||||
yaml_INT_TAG = "tag:yaml.org,2002:int"
|
||||
/** The tag @c !!float for float values. */
|
||||
yaml_FLOAT_TAG = "tag:yaml.org,2002:float"
|
||||
/** The tag @c !!timestamp for date and time values. */
|
||||
yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp"
|
||||
|
||||
/** The tag @c !!seq is used to denote sequences. */
|
||||
yaml_SEQ_TAG = "tag:yaml.org,2002:seq"
|
||||
/** The tag @c !!map is used to denote mapping. */
|
||||
yaml_MAP_TAG = "tag:yaml.org,2002:map"
|
||||
|
||||
/** The default scalar tag is @c !!str. */
|
||||
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG
|
||||
/** The default sequence tag is @c !!seq. */
|
||||
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG
|
||||
/** The default mapping tag is @c !!map. */
|
||||
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG
|
||||
|
||||
yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
|
||||
)
|
||||
|
||||
/** Node types. */
|
||||
type yaml_node_type_t int
|
||||
|
||||
const (
|
||||
/** An empty node. */
|
||||
yaml_NO_NODE yaml_node_type_t = iota
|
||||
|
||||
/** A scalar node. */
|
||||
yaml_SCALAR_NODE
|
||||
/** A sequence node. */
|
||||
yaml_SEQUENCE_NODE
|
||||
/** A mapping node. */
|
||||
yaml_MAPPING_NODE
|
||||
)
|
||||
|
||||
/** An element of a sequence node. */
|
||||
type yaml_node_item_t int
|
||||
|
||||
/** An element of a mapping node. */
|
||||
type yaml_node_pair_t struct {
|
||||
/** The key of the element. */
|
||||
key int
|
||||
/** The value of the element. */
|
||||
value int
|
||||
}
|
||||
|
||||
/** The node structure. */
|
||||
type yaml_node_t struct {
|
||||
|
||||
/** The node type. */
|
||||
node_type yaml_node_type_t
|
||||
|
||||
/** The node tag. */
|
||||
tag []byte
|
||||
|
||||
/** The scalar parameters (for @c yaml_SCALAR_NODE). */
|
||||
scalar struct {
|
||||
/** The scalar value. */
|
||||
value []byte
|
||||
/** The scalar style. */
|
||||
style yaml_scalar_style_t
|
||||
}
|
||||
|
||||
/** The sequence parameters (for @c yaml_SEQUENCE_NODE). */
|
||||
sequence struct {
|
||||
/** The stack of sequence items. */
|
||||
items []yaml_node_item_t
|
||||
/** The sequence style. */
|
||||
style yaml_sequence_style_t
|
||||
}
|
||||
|
||||
/** The mapping parameters (for @c yaml_MAPPING_NODE). */
|
||||
mapping struct {
|
||||
/** The stack of mapping pairs (key, value). */
|
||||
pairs []yaml_node_pair_t
|
||||
/** The mapping style. */
|
||||
style yaml_mapping_style_t
|
||||
}
|
||||
|
||||
/** The beginning of the node. */
|
||||
start_mark YAML_mark_t
|
||||
/** The end of the node. */
|
||||
end_mark YAML_mark_t
|
||||
}
|
||||
|
||||
/** The document structure. */
|
||||
type yaml_document_t struct {
|
||||
|
||||
/** The document nodes. */
|
||||
nodes []yaml_node_t
|
||||
|
||||
/** The version directive. */
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
/** The list of tag directives. */
|
||||
tags []yaml_tag_directive_t
|
||||
|
||||
/** Is the document start indicator implicit? */
|
||||
start_implicit bool
|
||||
/** Is the document end indicator implicit? */
|
||||
end_implicit bool
|
||||
|
||||
/** The beginning of the document. */
|
||||
start_mark YAML_mark_t
|
||||
/** The end of the document. */
|
||||
end_mark YAML_mark_t
|
||||
}
|
||||
|
||||
/**
|
||||
* The prototype of a read handler.
|
||||
*
|
||||
* The read handler is called when the parser needs to read more bytes from the
|
||||
* source. The handler should write not more than @a size bytes to the @a
|
||||
* buffer. The number of written bytes should be set to the @a length variable.
|
||||
*
|
||||
* @param[in,out] data A pointer to an application data specified by
|
||||
* yaml_parser_set_input().
|
||||
* @param[out] buffer The buffer to write the data from the source.
|
||||
* @param[in] size The size of the buffer.
|
||||
* @param[out] size_read The actual number of bytes read from the source.
|
||||
*
|
||||
* @returns On success, the handler should return @c 1. If the handler failed,
|
||||
* the returned value should be @c 0. On EOF, the handler should set the
|
||||
* @a size_read to @c 0 and return @c 1.
|
||||
*/
|
||||
|
||||
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
|
||||
|
||||
/**
|
||||
* This structure holds information about a potential simple key.
|
||||
*/
|
||||
|
||||
type yaml_simple_key_t struct {
|
||||
/** Is a simple key possible? */
|
||||
possible bool
|
||||
|
||||
/** Is a simple key required? */
|
||||
required bool
|
||||
|
||||
/** The number of the token. */
|
||||
token_number int
|
||||
|
||||
/** The position mark. */
|
||||
mark YAML_mark_t
|
||||
}
|
||||
|
||||
/**
|
||||
* The states of the parser.
|
||||
*/
|
||||
type yaml_parser_state_t int
|
||||
|
||||
const (
|
||||
/** Expect STREAM-START. */
|
||||
yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
|
||||
/** Expect the beginning of an implicit document. */
|
||||
yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE
|
||||
/** Expect DOCUMENT-START. */
|
||||
yaml_PARSE_DOCUMENT_START_STATE
|
||||
/** Expect the content of a document. */
|
||||
yaml_PARSE_DOCUMENT_CONTENT_STATE
|
||||
/** Expect DOCUMENT-END. */
|
||||
yaml_PARSE_DOCUMENT_END_STATE
|
||||
/** Expect a block node. */
|
||||
yaml_PARSE_BLOCK_NODE_STATE
|
||||
/** Expect a block node or indentless sequence. */
|
||||
yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE
|
||||
/** Expect a flow node. */
|
||||
yaml_PARSE_FLOW_NODE_STATE
|
||||
/** Expect the first entry of a block sequence. */
|
||||
yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE
|
||||
/** Expect an entry of a block sequence. */
|
||||
yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE
|
||||
/** Expect an entry of an indentless sequence. */
|
||||
yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
|
||||
/** Expect the first key of a block mapping. */
|
||||
yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE
|
||||
/** Expect a block mapping key. */
|
||||
yaml_PARSE_BLOCK_MAPPING_KEY_STATE
|
||||
/** Expect a block mapping value. */
|
||||
yaml_PARSE_BLOCK_MAPPING_VALUE_STATE
|
||||
/** Expect the first entry of a flow sequence. */
|
||||
yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE
|
||||
/** Expect an entry of a flow sequence. */
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE
|
||||
/** Expect a key of an ordered mapping. */
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE
|
||||
/** Expect a value of an ordered mapping. */
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE
|
||||
/** Expect the and of an ordered mapping entry. */
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE
|
||||
/** Expect the first key of a flow mapping. */
|
||||
yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE
|
||||
/** Expect a key of a flow mapping. */
|
||||
yaml_PARSE_FLOW_MAPPING_KEY_STATE
|
||||
/** Expect a value of a flow mapping. */
|
||||
yaml_PARSE_FLOW_MAPPING_VALUE_STATE
|
||||
/** Expect an empty value of a flow mapping. */
|
||||
yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE
|
||||
/** Expect nothing. */
|
||||
yaml_PARSE_END_STATE
|
||||
)
|
||||
|
||||
/**
|
||||
* This structure holds aliases data.
|
||||
*/
|
||||
|
||||
type yaml_alias_data_t struct {
|
||||
/** The anchor. */
|
||||
anchor []byte
|
||||
/** The node id. */
|
||||
index int
|
||||
/** The anchor mark. */
|
||||
mark YAML_mark_t
|
||||
}
|
||||
|
||||
/**
|
||||
* The parser structure.
|
||||
*
|
||||
* All members are internal. Manage the structure using the @c yaml_parser_
|
||||
* family of functions.
|
||||
*/
|
||||
|
||||
type yaml_parser_t struct {
|
||||
|
||||
/**
|
||||
* @name Error handling
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Error type. */
|
||||
error YAML_error_type_t
|
||||
/** Error description. */
|
||||
problem string
|
||||
/** The byte about which the problem occured. */
|
||||
problem_offset int
|
||||
/** The problematic value (@c -1 is none). */
|
||||
problem_value int
|
||||
/** The problem position. */
|
||||
problem_mark YAML_mark_t
|
||||
/** The error context. */
|
||||
context string
|
||||
/** The context position. */
|
||||
context_mark YAML_mark_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Reader stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Read handler. */
|
||||
read_handler yaml_read_handler_t
|
||||
|
||||
/** Reader input data. */
|
||||
input_reader io.Reader
|
||||
input []byte
|
||||
input_pos int
|
||||
|
||||
/** EOF flag */
|
||||
eof bool
|
||||
|
||||
/** The working buffer. */
|
||||
buffer []byte
|
||||
buffer_pos int
|
||||
|
||||
/* The number of unread characters in the buffer. */
|
||||
unread int
|
||||
|
||||
/** The raw buffer. */
|
||||
raw_buffer []byte
|
||||
raw_buffer_pos int
|
||||
|
||||
/** The input encoding. */
|
||||
encoding yaml_encoding_t
|
||||
|
||||
/** The offset of the current position (in bytes). */
|
||||
offset int
|
||||
|
||||
/** The mark of the current position. */
|
||||
mark YAML_mark_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Scanner stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Have we started to scan the input stream? */
|
||||
stream_start_produced bool
|
||||
|
||||
/** Have we reached the end of the input stream? */
|
||||
stream_end_produced bool
|
||||
|
||||
/** The number of unclosed '[' and '{' indicators. */
|
||||
flow_level int
|
||||
|
||||
/** The tokens queue. */
|
||||
tokens []yaml_token_t
|
||||
tokens_head int
|
||||
|
||||
/** The number of tokens fetched from the queue. */
|
||||
tokens_parsed int
|
||||
|
||||
/* Does the tokens queue contain a token ready for dequeueing. */
|
||||
token_available bool
|
||||
|
||||
/** The indentation levels stack. */
|
||||
indents []int
|
||||
|
||||
/** The current indentation level. */
|
||||
indent int
|
||||
|
||||
/** May a simple key occur at the current position? */
|
||||
simple_key_allowed bool
|
||||
|
||||
/** The stack of simple keys. */
|
||||
simple_keys []yaml_simple_key_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Parser stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** The parser states stack. */
|
||||
states []yaml_parser_state_t
|
||||
|
||||
/** The current parser state. */
|
||||
state yaml_parser_state_t
|
||||
|
||||
/** The stack of marks. */
|
||||
marks []YAML_mark_t
|
||||
|
||||
/** The list of TAG directives. */
|
||||
tag_directives []yaml_tag_directive_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Dumper stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** The alias data. */
|
||||
aliases []yaml_alias_data_t
|
||||
|
||||
/** The currently parsed document. */
|
||||
document *yaml_document_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The prototype of a write handler.
|
||||
*
|
||||
* The write handler is called when the emitter needs to flush the accumulated
|
||||
* characters to the output. The handler should write @a size bytes of the
|
||||
* @a buffer to the output.
|
||||
*
|
||||
* @param[in,out] data A pointer to an application data specified by
|
||||
* yaml_emitter_set_output().
|
||||
* @param[in] buffer The buffer with bytes to be written.
|
||||
* @param[in] size The size of the buffer.
|
||||
*
|
||||
* @returns On success, the handler should return @c 1. If the handler failed,
|
||||
* the returned value should be @c 0.
|
||||
*/
|
||||
|
||||
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
|
||||
|
||||
/** The emitter states. */
|
||||
type yaml_emitter_state_t int
|
||||
|
||||
const (
|
||||
/** Expect STREAM-START. */
|
||||
yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
|
||||
/** Expect the first DOCUMENT-START or STREAM-END. */
|
||||
yaml_EMIT_FIRST_DOCUMENT_START_STATE
|
||||
/** Expect DOCUMENT-START or STREAM-END. */
|
||||
yaml_EMIT_DOCUMENT_START_STATE
|
||||
/** Expect the content of a document. */
|
||||
yaml_EMIT_DOCUMENT_CONTENT_STATE
|
||||
/** Expect DOCUMENT-END. */
|
||||
yaml_EMIT_DOCUMENT_END_STATE
|
||||
/** Expect the first item of a flow sequence. */
|
||||
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE
|
||||
/** Expect an item of a flow sequence. */
|
||||
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE
|
||||
/** Expect the first key of a flow mapping. */
|
||||
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE
|
||||
/** Expect a key of a flow mapping. */
|
||||
yaml_EMIT_FLOW_MAPPING_KEY_STATE
|
||||
/** Expect a value for a simple key of a flow mapping. */
|
||||
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE
|
||||
/** Expect a value of a flow mapping. */
|
||||
yaml_EMIT_FLOW_MAPPING_VALUE_STATE
|
||||
/** Expect the first item of a block sequence. */
|
||||
yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE
|
||||
/** Expect an item of a block sequence. */
|
||||
yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE
|
||||
/** Expect the first key of a block mapping. */
|
||||
yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE
|
||||
/** Expect the key of a block mapping. */
|
||||
yaml_EMIT_BLOCK_MAPPING_KEY_STATE
|
||||
/** Expect a value for a simple key of a block mapping. */
|
||||
yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE
|
||||
/** Expect a value of a block mapping. */
|
||||
yaml_EMIT_BLOCK_MAPPING_VALUE_STATE
|
||||
/** Expect nothing. */
|
||||
yaml_EMIT_END_STATE
|
||||
)
|
||||
|
||||
/**
|
||||
* The emitter structure.
|
||||
*
|
||||
* All members are internal. Manage the structure using the @c yaml_emitter_
|
||||
* family of functions.
|
||||
*/
|
||||
|
||||
type yaml_emitter_t struct {
|
||||
|
||||
/**
|
||||
* @name Error handling
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Error type. */
|
||||
error YAML_error_type_t
|
||||
/** Error description. */
|
||||
problem string
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Writer stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** Write handler. */
|
||||
write_handler yaml_write_handler_t
|
||||
|
||||
/** Standard (string or file) output data. */
|
||||
output_buffer *[]byte
|
||||
output_writer io.Writer
|
||||
|
||||
/** The working buffer. */
|
||||
buffer []byte
|
||||
buffer_pos int
|
||||
|
||||
/** The raw buffer. */
|
||||
raw_buffer []byte
|
||||
raw_buffer_pos int
|
||||
|
||||
/** The stream encoding. */
|
||||
encoding yaml_encoding_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Emitter stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** If the output is in the canonical style? */
|
||||
canonical bool
|
||||
/** The number of indentation spaces. */
|
||||
best_indent int
|
||||
/** The preferred width of the output lines. */
|
||||
best_width int
|
||||
/** Allow unescaped non-ASCII characters? */
|
||||
unicode bool
|
||||
/** The preferred line break. */
|
||||
line_break yaml_break_t
|
||||
|
||||
/** The stack of states. */
|
||||
states []yaml_emitter_state_t
|
||||
|
||||
/** The current emitter state. */
|
||||
state yaml_emitter_state_t
|
||||
|
||||
/** The event queue. */
|
||||
events []yaml_event_t
|
||||
events_head int
|
||||
|
||||
/** The stack of indentation levels. */
|
||||
indents []int
|
||||
|
||||
/** The list of tag directives. */
|
||||
tag_directives []yaml_tag_directive_t
|
||||
|
||||
/** The current indentation level. */
|
||||
indent int
|
||||
|
||||
/** The current flow level. */
|
||||
flow_level int
|
||||
|
||||
/** Is it the document root context? */
|
||||
root_context bool
|
||||
/** Is it a sequence context? */
|
||||
sequence_context bool
|
||||
/** Is it a mapping context? */
|
||||
mapping_context bool
|
||||
/** Is it a simple mapping key context? */
|
||||
simple_key_context bool
|
||||
|
||||
/** The current line. */
|
||||
line int
|
||||
/** The current column. */
|
||||
column int
|
||||
/** If the last character was a whitespace? */
|
||||
whitespace bool
|
||||
/** If the last character was an indentation character (' ', '-', '?', ':')? */
|
||||
indention bool
|
||||
/** If an explicit document end is required? */
|
||||
open_ended bool
|
||||
|
||||
/** Anchor analysis. */
|
||||
anchor_data struct {
|
||||
/** The anchor value. */
|
||||
anchor []byte
|
||||
/** Is it an alias? */
|
||||
alias bool
|
||||
}
|
||||
|
||||
/** Tag analysis. */
|
||||
tag_data struct {
|
||||
/** The tag handle. */
|
||||
handle []byte
|
||||
/** The tag suffix. */
|
||||
suffix []byte
|
||||
}
|
||||
|
||||
/** Scalar analysis. */
|
||||
scalar_data struct {
|
||||
/** The scalar value. */
|
||||
value []byte
|
||||
/** Does the scalar contain line breaks? */
|
||||
multiline bool
|
||||
/** Can the scalar be expessed in the flow plain style? */
|
||||
flow_plain_allowed bool
|
||||
/** Can the scalar be expressed in the block plain style? */
|
||||
block_plain_allowed bool
|
||||
/** Can the scalar be expressed in the single quoted style? */
|
||||
single_quoted_allowed bool
|
||||
/** Can the scalar be expressed in the literal or folded styles? */
|
||||
block_allowed bool
|
||||
/** The output style. */
|
||||
style yaml_scalar_style_t
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Dumper stuff
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** If the stream was already opened? */
|
||||
opened bool
|
||||
/** If the stream was already closed? */
|
||||
closed bool
|
||||
|
||||
/** The information associated with the document nodes. */
|
||||
anchors *struct {
|
||||
/** The number of references. */
|
||||
references int
|
||||
/** The anchor id. */
|
||||
anchor int
|
||||
/** If the node has been emitted? */
|
||||
serialized bool
|
||||
}
|
||||
|
||||
/** The last assigned anchor id. */
|
||||
last_anchor_id int
|
||||
|
||||
/** The currently emitted document. */
|
||||
document *yaml_document_t
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package backend
|
||||
|
||||
import "io"
|
||||
|
||||
// Engine defines a container orchestration backend and is used
|
||||
// to create and manage container resources.
|
||||
type Engine interface {
|
||||
// Setup the pipeline environment.
|
||||
Setup(*Config) error
|
||||
// Start the pipeline step.
|
||||
Exec(*Step) error
|
||||
// Kill the pipeline step.
|
||||
Kill(*Step) error
|
||||
// Wait for the pipeline step to complete and returns
|
||||
// the completion results.
|
||||
Wait(*Step) (*State, error)
|
||||
// Tail the pipeline step logs.
|
||||
Tail(*Step) (io.ReadCloser, error)
|
||||
// Destroy the pipeline environment.
|
||||
Destroy(*Config) error
|
||||
}
|
130
vendor/github.com/cncd/pipeline/pipeline/backend/docker/convert.go
generated
vendored
Normal file
130
vendor/github.com/cncd/pipeline/pipeline/backend/docker/convert.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/cncd/pipeline/pipeline/backend"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
// returns a container configuration.
|
||||
func toConfig(proc *backend.Step) *container.Config {
|
||||
config := &container.Config{
|
||||
Image: proc.Image,
|
||||
Labels: proc.Labels,
|
||||
WorkingDir: proc.WorkingDir,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
}
|
||||
if len(proc.Environment) != 0 {
|
||||
config.Env = toEnv(proc.Environment)
|
||||
}
|
||||
if len(proc.Command) != 0 {
|
||||
config.Cmd = proc.Command
|
||||
}
|
||||
if len(proc.Entrypoint) != 0 {
|
||||
config.Entrypoint = proc.Entrypoint
|
||||
}
|
||||
if len(proc.Volumes) != 0 {
|
||||
config.Volumes = toVol(proc.Volumes)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// returns a container host configuration.
|
||||
func toHostConfig(proc *backend.Step) *container.HostConfig {
|
||||
config := &container.HostConfig{
|
||||
Resources: container.Resources{
|
||||
CPUQuota: proc.CPUQuota,
|
||||
CPUShares: proc.CPUShares,
|
||||
CpusetCpus: proc.CPUSet,
|
||||
Memory: proc.MemLimit,
|
||||
MemorySwap: proc.MemSwapLimit,
|
||||
},
|
||||
Privileged: proc.Privileged,
|
||||
ShmSize: proc.ShmSize,
|
||||
}
|
||||
// if len(proc.VolumesFrom) != 0 {
|
||||
// config.VolumesFrom = proc.VolumesFrom
|
||||
// }
|
||||
// if len(proc.Network) != 0 {
|
||||
// config.NetworkMode = container.NetworkMode(
|
||||
// proc.Network,
|
||||
// )
|
||||
// }
|
||||
if len(proc.DNS) != 0 {
|
||||
config.DNS = proc.DNS
|
||||
}
|
||||
if len(proc.DNSSearch) != 0 {
|
||||
config.DNSSearch = proc.DNSSearch
|
||||
}
|
||||
if len(proc.ExtraHosts) != 0 {
|
||||
config.ExtraHosts = proc.ExtraHosts
|
||||
}
|
||||
if len(proc.Devices) != 0 {
|
||||
config.Devices = toDev(proc.Devices)
|
||||
}
|
||||
if len(proc.Volumes) != 0 {
|
||||
config.Binds = proc.Volumes
|
||||
}
|
||||
// if proc.OomKillDisable {
|
||||
// config.OomKillDisable = &proc.OomKillDisable
|
||||
// }
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// helper function that converts a slice of volume paths to a set of
|
||||
// unique volume names.
|
||||
func toVol(paths []string) map[string]struct{} {
|
||||
set := map[string]struct{}{}
|
||||
for _, path := range paths {
|
||||
parts := strings.Split(path, ":")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
set[parts[1]] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// helper function that converts a key value map of environment variables to a
|
||||
// string slice in key=value format.
|
||||
func toEnv(env map[string]string) []string {
|
||||
var envs []string
|
||||
for k, v := range env {
|
||||
envs = append(envs, k+"="+v)
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
// helper function that converts a slice of device paths to a slice of
|
||||
// container.DeviceMapping.
|
||||
func toDev(paths []string) []container.DeviceMapping {
|
||||
var devices []container.DeviceMapping
|
||||
for _, path := range paths {
|
||||
parts := strings.Split(path, ":")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
devices = append(devices, container.DeviceMapping{
|
||||
PathOnHost: parts[0],
|
||||
PathInContainer: parts[1],
|
||||
CgroupPermissions: "rwm",
|
||||
})
|
||||
}
|
||||
return devices
|
||||
}
|
||||
|
||||
// helper function that serializes the auth configuration as JSON
|
||||
// base64 payload.
|
||||
func encodeAuthToBase64(authConfig backend.Auth) (string, error) {
|
||||
buf, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(buf), nil
|
||||
}
|
202
vendor/github.com/cncd/pipeline/pipeline/backend/docker/docker.go
generated
vendored
Normal file
202
vendor/github.com/cncd/pipeline/pipeline/backend/docker/docker.go
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/cncd/pipeline/pipeline/backend"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
type engine struct {
|
||||
client client.APIClient
|
||||
}
|
||||
|
||||
// New returns a new Docker Engine using the given client.
|
||||
func New(cli client.APIClient) backend.Engine {
|
||||
return &engine{
|
||||
client: cli,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEnv returns a new Docker Engine using the client connection
|
||||
// environment variables.
|
||||
func NewEnv() (backend.Engine, error) {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return New(cli), nil
|
||||
}
|
||||
|
||||
func (e *engine) Setup(conf *backend.Config) error {
|
||||
for _, vol := range conf.Volumes {
|
||||
_, err := e.client.VolumeCreate(noContext, volume.VolumesCreateBody{
|
||||
Name: vol.Name,
|
||||
Driver: vol.Driver,
|
||||
DriverOpts: vol.DriverOpts,
|
||||
// Labels: defaultLabels,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, network := range conf.Networks {
|
||||
_, err := e.client.NetworkCreate(noContext, network.Name, types.NetworkCreate{
|
||||
Driver: network.Driver,
|
||||
Options: network.DriverOpts,
|
||||
// Labels: defaultLabels,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *engine) Exec(proc *backend.Step) error {
|
||||
ctx := context.Background()
|
||||
|
||||
config := toConfig(proc)
|
||||
hostConfig := toHostConfig(proc)
|
||||
|
||||
// create pull options with encoded authorization credentials.
|
||||
pullopts := types.ImagePullOptions{}
|
||||
if proc.AuthConfig.Username != "" && proc.AuthConfig.Password != "" {
|
||||
pullopts.RegistryAuth, _ = encodeAuthToBase64(proc.AuthConfig)
|
||||
}
|
||||
|
||||
// automatically pull the latest version of the image if requested
|
||||
// by the process configuration.
|
||||
if proc.Pull {
|
||||
rc, perr := e.client.ImagePull(ctx, config.Image, pullopts)
|
||||
if perr == nil {
|
||||
io.Copy(ioutil.Discard, rc)
|
||||
rc.Close()
|
||||
}
|
||||
// fix for drone/drone#1917
|
||||
if perr != nil && proc.AuthConfig.Password != "" {
|
||||
return perr
|
||||
}
|
||||
}
|
||||
|
||||
_, err := e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
|
||||
if client.IsErrImageNotFound(err) {
|
||||
// automatically pull and try to re-create the image if the
|
||||
// failure is caused because the image does not exist.
|
||||
rc, perr := e.client.ImagePull(ctx, config.Image, pullopts)
|
||||
if perr != nil {
|
||||
return perr
|
||||
}
|
||||
io.Copy(ioutil.Discard, rc)
|
||||
rc.Close()
|
||||
|
||||
_, err = e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, net := range proc.Networks {
|
||||
err = e.client.NetworkConnect(ctx, net.Name, proc.Name, &network.EndpointSettings{
|
||||
Aliases: net.Aliases,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if proc.Network != "host" { // or bridge, overlay, none, internal, container:<name> ....
|
||||
// err = e.client.NetworkConnect(ctx, proc.Network, proc.Name, &network.EndpointSettings{
|
||||
// Aliases: proc.NetworkAliases,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
return e.client.ContainerStart(ctx, proc.Name, startOpts)
|
||||
}
|
||||
|
||||
func (e *engine) Kill(proc *backend.Step) error {
|
||||
return e.client.ContainerKill(noContext, proc.Name, "9")
|
||||
}
|
||||
|
||||
func (e *engine) Wait(proc *backend.Step) (*backend.State, error) {
|
||||
_, err := e.client.ContainerWait(noContext, proc.Name)
|
||||
if err != nil {
|
||||
// todo
|
||||
}
|
||||
|
||||
info, err := e.client.ContainerInspect(noContext, proc.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.State.Running {
|
||||
// todo
|
||||
}
|
||||
|
||||
return &backend.State{
|
||||
Exited: true,
|
||||
ExitCode: info.State.ExitCode,
|
||||
OOMKilled: info.State.OOMKilled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *engine) Tail(proc *backend.Step) (io.ReadCloser, error) {
|
||||
logs, err := e.client.ContainerLogs(noContext, proc.Name, logsOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc, wc := io.Pipe()
|
||||
|
||||
go func() {
|
||||
stdcopy.StdCopy(wc, wc, logs)
|
||||
logs.Close()
|
||||
wc.Close()
|
||||
rc.Close()
|
||||
}()
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
func (e *engine) Destroy(conf *backend.Config) error {
|
||||
for _, stage := range conf.Stages {
|
||||
for _, step := range stage.Steps {
|
||||
e.client.ContainerKill(noContext, step.Name, "9")
|
||||
e.client.ContainerRemove(noContext, step.Name, removeOpts)
|
||||
}
|
||||
}
|
||||
for _, volume := range conf.Volumes {
|
||||
e.client.VolumeRemove(noContext, volume.Name, true)
|
||||
}
|
||||
for _, network := range conf.Networks {
|
||||
e.client.NetworkRemove(noContext, network.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
noContext = context.Background()
|
||||
|
||||
startOpts = types.ContainerStartOptions{}
|
||||
|
||||
removeOpts = types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
RemoveLinks: false,
|
||||
Force: false,
|
||||
}
|
||||
|
||||
logsOpts = types.ContainerLogsOptions{
|
||||
Follow: true,
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Details: false,
|
||||
Timestamps: false,
|
||||
}
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
package docker
|
||||
|
||||
// import (
|
||||
// "context"
|
||||
//
|
||||
// "github.com/cncd/pipeline/pipeline/backend"
|
||||
// )
|
||||
//
|
||||
// // Pool manages a pool of Docker clients.
|
||||
// type Pool struct {
|
||||
// queue chan (backend.Engine)
|
||||
// }
|
||||
//
|
||||
// // NewPool returns a Pool.
|
||||
// func NewPool(engines ...backend.Engine) *Pool {
|
||||
// return &Pool{
|
||||
// queue: make(chan backend.Engine, len(engines)),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Reserve requests the next available Docker client in the pool.
|
||||
// func (p *Pool) Reserve(c context.Context) backend.Engine {
|
||||
// select {
|
||||
// case <-c.Done():
|
||||
// case engine := <-p.queue:
|
||||
// return engine
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// // Release releases the Docker client back to the pool.
|
||||
// func (p *Pool) Release(engine backend.Engine) {
|
||||
// p.queue <- engine
|
||||
// }
|
||||
|
||||
// pool := docker.Pool(
|
||||
// docker.FromEnvironmentMust(),
|
||||
// docker.FromEnvironmentMust(),
|
||||
// docker.FromEnvironmentMust(),
|
||||
// docker.FromEnvironmentMust(),
|
||||
// )
|
||||
//
|
||||
// client := pool.Reserve()
|
||||
// defer pool.Release(client)
|
|
@ -0,0 +1,112 @@
|
|||
package backend
|
||||
|
||||
type (
|
||||
// Config defines the runtime configuration of a pipeline.
|
||||
Config struct {
|
||||
Stages []*Stage `json:"pipeline"` // pipeline stages
|
||||
Networks []*Network `json:"networks"` // network definitions
|
||||
Volumes []*Volume `json:"volumes"` // volume definitions
|
||||
Secrets []*Secret `json:"secrets"` // secret definitions
|
||||
}
|
||||
|
||||
// Stage denotes a collection of one or more steps.
|
||||
Stage struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Steps []*Step `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// Step defines a container process.
|
||||
Step struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Image string `json:"image,omitempty"`
|
||||
Pull bool `json:"pull,omitempty"`
|
||||
Detached bool `json:"detach,omitempty"`
|
||||
Privileged bool `json:"privileged,omitempty"`
|
||||
WorkingDir string `json:"working_dir,omitempty"`
|
||||
Environment map[string]string `json:"environment,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Entrypoint []string `json:"entrypoint,omitempty"`
|
||||
Command []string `json:"command,omitempty"`
|
||||
ExtraHosts []string `json:"extra_hosts,omitempty"`
|
||||
Volumes []string `json:"volumes,omitempty"`
|
||||
Devices []string `json:"devices,omitempty"`
|
||||
Networks []Conn `json:"networks,omitempty"`
|
||||
DNS []string `json:"dns,omitempty"`
|
||||
DNSSearch []string `json:"dns_search,omitempty"`
|
||||
MemSwapLimit int64 `json:"memswap_limit,omitempty"`
|
||||
MemLimit int64 `json:"mem_limit,omitempty"`
|
||||
ShmSize int64 `json:"shm_size,omitempty"`
|
||||
CPUQuota int64 `json:"cpu_quota,omitempty"`
|
||||
CPUShares int64 `json:"cpu_shares,omitempty"`
|
||||
CPUSet string `json:"cpu_set,omitempty"`
|
||||
OnFailure bool `json:"on_failure,omitempty"`
|
||||
OnSuccess bool `json:"on_success,omitempty"`
|
||||
AuthConfig Auth `json:"auth_config,omitempty"`
|
||||
}
|
||||
|
||||
// Auth defines registry authentication credentials.
|
||||
Auth struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// Conn defines a container network connection.
|
||||
Conn struct {
|
||||
Name string `json:"name"`
|
||||
Aliases []string `json:"aliases"`
|
||||
}
|
||||
|
||||
// Network defines a container network.
|
||||
Network struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Driver string `json:"driver,omitempty"`
|
||||
DriverOpts map[string]string `json:"driver_opts,omitempty"`
|
||||
}
|
||||
|
||||
// Volume defines a container volume.
|
||||
Volume struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Driver string `json:"driver,omitempty"`
|
||||
DriverOpts map[string]string `json:"driver_opts,omitempty"`
|
||||
}
|
||||
|
||||
// Secret defines a runtime secret
|
||||
Secret struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Mount string `json:"mount,omitempty"`
|
||||
Mask bool `json:"mask,omitempty"`
|
||||
}
|
||||
|
||||
// State defines a container state.
|
||||
State struct {
|
||||
// Container exit code
|
||||
ExitCode int `json:"exit_code"`
|
||||
// Container exited, true or false
|
||||
Exited bool `json:"exited"`
|
||||
// Container is oom killed, true or false
|
||||
OOMKilled bool `json:"oom_killed"`
|
||||
}
|
||||
|
||||
// // State defines the pipeline and process state.
|
||||
// State struct {
|
||||
// Pipeline struct {
|
||||
// // Current pipeline step
|
||||
// Step *Step `json:"step"`
|
||||
// // Current pipeline error state
|
||||
// Error error `json:"error"`
|
||||
// }
|
||||
//
|
||||
// Process struct {
|
||||
// // Container exit code
|
||||
// ExitCode int `json:"exit_code"`
|
||||
// // Container exited, true or false
|
||||
// Exited bool `json:"exited"`
|
||||
// // Container is oom killed, true or false
|
||||
// OOMKilled bool `json:"oom_killed"`
|
||||
// }
|
||||
// }
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package build
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -8,11 +8,11 @@ import (
|
|||
var (
|
||||
// ErrSkip is used as a return value when container execution should be
|
||||
// skipped at runtime. It is not returned as an error by any function.
|
||||
ErrSkip = errors.New("Skip")
|
||||
ErrSkip = errors.New("Skipped")
|
||||
|
||||
// ErrTerm is used as a return value when the runner should terminate
|
||||
// execution and exit. It is not returned as an error by any function.
|
||||
ErrTerm = errors.New("Terminate")
|
||||
// ErrCancel is used as a return value when the container execution receives
|
||||
// a cancellation signal from the context.
|
||||
ErrCancel = errors.New("Cancelled")
|
||||
)
|
||||
|
||||
// An ExitError reports an unsuccessful exit.
|
||||
|
@ -21,7 +21,7 @@ type ExitError struct {
|
|||
Code int
|
||||
}
|
||||
|
||||
// Error reteurns the error message in string format.
|
||||
// Error returns the error message in string format.
|
||||
func (e *ExitError) Error() string {
|
||||
return fmt.Sprintf("%s : exit code %d", e.Name, e.Code)
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ func (e *ExitError) Error() string {
|
|||
// An OomError reports the process received an OOMKill from the kernel.
|
||||
type OomError struct {
|
||||
Name string
|
||||
Code int
|
||||
}
|
||||
|
||||
// Error reteurns the error message in string format.
|
|
@ -0,0 +1,223 @@
|
|||
package frontend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Event types corresponding to scm hooks.
|
||||
const (
|
||||
EventPush = "push"
|
||||
EventPull = "pull_request"
|
||||
EventTag = "tag"
|
||||
EventDeploy = "deployment"
|
||||
)
|
||||
|
||||
type (
|
||||
// Metadata defines runtime m.
|
||||
Metadata struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Repo Repo `json:"repo,omitempty"`
|
||||
Curr Build `json:"curr,omitempty"`
|
||||
Prev Build `json:"prev,omitempty"`
|
||||
Job Job `json:"job,omitempty"`
|
||||
Sys System `json:"sys,omitempty"`
|
||||
}
|
||||
|
||||
// Repo defines runtime metadata for a repository.
|
||||
Repo struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Link string `json:"link,omitempty"`
|
||||
Remote string `json:"remote,omitempty"`
|
||||
Private bool `json:"private,omitempty"`
|
||||
Secrets []Secret `json:"secrets,omitempty"`
|
||||
}
|
||||
|
||||
// Build defines runtime metadata for a build.
|
||||
Build struct {
|
||||
Number int `json:"number,omitempty"`
|
||||
Created int64 `json:"created,omitempty"`
|
||||
Started int64 `json:"started,omitempty"`
|
||||
Finished int64 `json:"finished,omitempty"`
|
||||
Timeout int64 `json:"timeout,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Event string `json:"event,omitempty"`
|
||||
Link string `json:"link,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Trusted bool `json:"trusted,omitempty"`
|
||||
Commit Commit `json:"commit,omitempty"`
|
||||
Parent int `json:"parent,omitempty"`
|
||||
}
|
||||
|
||||
// Commit defines runtime metadata for a commit.
|
||||
Commit struct {
|
||||
Sha string `json:"sha,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Refspec string `json:"refspec,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Author Author `json:"author,omitempty"`
|
||||
}
|
||||
|
||||
// Author defines runtime metadata for a commit author.
|
||||
Author struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
}
|
||||
|
||||
// Job defines runtime metadata for a job.
|
||||
Job struct {
|
||||
Number int `json:"number,omitempty"`
|
||||
Matrix map[string]string `json:"matrix,omitempty"`
|
||||
}
|
||||
|
||||
// Secret defines a runtime secret
|
||||
Secret struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Mount string `json:"mount,omitempty"`
|
||||
Mask bool `json:"mask,omitempty"`
|
||||
}
|
||||
|
||||
// System defines runtime metadata for a ci/cd system.
|
||||
System struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Link string `json:"link,omitempty"`
|
||||
Arch string `json:"arch,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
// Environ returns the metadata as a map of environment variables.
|
||||
func (m *Metadata) Environ() map[string]string {
|
||||
params := map[string]string{
|
||||
"CI_REPO": m.Repo.Name,
|
||||
"CI_REPO_NAME": m.Repo.Name,
|
||||
"CI_REPO_LINK": m.Repo.Link,
|
||||
"CI_REPO_REMOTE": m.Repo.Remote,
|
||||
"CI_REMOTE_URL": m.Repo.Remote,
|
||||
"CI_REPO_PRIVATE": strconv.FormatBool(m.Repo.Private),
|
||||
"CI_BUILD_NUMBER": strconv.Itoa(m.Curr.Number),
|
||||
"CI_PARENT_BUILD_NUMBER": strconv.Itoa(m.Curr.Parent),
|
||||
"CI_BUILD_CREATED": strconv.FormatInt(m.Curr.Created, 10),
|
||||
"CI_BUILD_STARTED": strconv.FormatInt(m.Curr.Started, 10),
|
||||
"CI_BUILD_FINISHED": strconv.FormatInt(m.Curr.Finished, 10),
|
||||
"CI_BUILD_STATUS": m.Curr.Status,
|
||||
"CI_BUILD_EVENT": m.Curr.Event,
|
||||
"CI_BUILD_LINK": m.Curr.Link,
|
||||
"CI_BUILD_TARGET": m.Curr.Target,
|
||||
"CI_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||
"CI_COMMIT_REF": m.Curr.Commit.Ref,
|
||||
"CI_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
||||
"CI_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
||||
"CI_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
||||
"CI_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
||||
"CI_COMMIT_AUTHOR_NAME": m.Curr.Commit.Author.Name,
|
||||
"CI_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
||||
"CI_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
||||
"CI_PREV_BUILD_NUMBER": strconv.Itoa(m.Prev.Number),
|
||||
"CI_PREV_BUILD_CREATED": strconv.FormatInt(m.Prev.Created, 10),
|
||||
"CI_PREV_BUILD_STARTED": strconv.FormatInt(m.Prev.Started, 10),
|
||||
"CI_PREV_BUILD_FINISHED": strconv.FormatInt(m.Prev.Finished, 10),
|
||||
"CI_PREV_BUILD_STATUS": m.Prev.Status,
|
||||
"CI_PREV_BUILD_EVENT": m.Prev.Event,
|
||||
"CI_PREV_BUILD_LINK": m.Prev.Link,
|
||||
"CI_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
||||
"CI_PREV_COMMIT_REF": m.Prev.Commit.Ref,
|
||||
"CI_PREV_COMMIT_REFSPEC": m.Prev.Commit.Refspec,
|
||||
"CI_PREV_COMMIT_BRANCH": m.Prev.Commit.Branch,
|
||||
"CI_PREV_COMMIT_MESSAGE": m.Prev.Commit.Message,
|
||||
"CI_PREV_COMMIT_AUTHOR": m.Prev.Commit.Author.Name,
|
||||
"CI_PREV_COMMIT_AUTHOR_NAME": m.Prev.Commit.Author.Name,
|
||||
"CI_PREV_COMMIT_AUTHOR_EMAIL": m.Prev.Commit.Author.Email,
|
||||
"CI_PREV_COMMIT_AUTHOR_AVATAR": m.Prev.Commit.Author.Avatar,
|
||||
"CI_JOB_NUMBER": strconv.Itoa(m.Job.Number),
|
||||
"CI_SYSTEM": m.Sys.Name,
|
||||
"CI_SYSTEM_NAME": m.Sys.Name,
|
||||
"CI_SYSTEM_LINK": m.Sys.Link,
|
||||
"CI_SYSTEM_HOST": m.Sys.Host,
|
||||
"CI_SYSTEM_ARCH": m.Sys.Arch,
|
||||
"CI_SYSTEM_VERSION": m.Sys.Version,
|
||||
"CI": m.Sys.Name,
|
||||
}
|
||||
if m.Curr.Event == EventTag {
|
||||
params["CI_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
||||
}
|
||||
if m.Curr.Event == EventPull {
|
||||
params["CI_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// EnvironDrone returns metadata as a map of DRONE_ environment variables.
|
||||
// TODO: This is here for backward compatibility and will eventually be removed.
|
||||
func (m *Metadata) EnvironDrone() map[string]string {
|
||||
// MISSING PARAMETERS
|
||||
// * DRONE_REPO_TRUSTED
|
||||
// * DRONE_YAML_VERIFIED
|
||||
// * DRONE_YAML_VERIFIED
|
||||
var (
|
||||
owner string
|
||||
name string
|
||||
|
||||
parts = strings.Split(m.Repo.Name, "/")
|
||||
)
|
||||
if len(parts) == 2 {
|
||||
owner = strings.Split(m.Repo.Name, "/")[0]
|
||||
name = strings.Split(m.Repo.Name, "/")[1]
|
||||
} else {
|
||||
name = m.Repo.Name
|
||||
}
|
||||
params := map[string]string{
|
||||
"CI": "drone",
|
||||
"DRONE": "true",
|
||||
"DRONE_ARCH": "linux/amd64",
|
||||
"DRONE_REPO": m.Repo.Name,
|
||||
"DRONE_REPO_SCM": "git",
|
||||
"DRONE_REPO_OWNER": owner,
|
||||
"DRONE_REPO_NAME": name,
|
||||
"DRONE_REPO_LINK": m.Repo.Link,
|
||||
"DRONE_REPO_BRANCH": m.Curr.Commit.Branch,
|
||||
"DRONE_REPO_PRIVATE": fmt.Sprintf("%v", m.Repo.Private),
|
||||
"DRONE_REPO_TRUSTED": "false", // TODO should this be added?
|
||||
"DRONE_REMOTE_URL": m.Repo.Remote,
|
||||
"DRONE_COMMIT_SHA": m.Curr.Commit.Sha,
|
||||
"DRONE_COMMIT_REF": m.Curr.Commit.Ref,
|
||||
"DRONE_COMMIT_REFSPEC": m.Curr.Commit.Refspec,
|
||||
"DRONE_COMMIT_BRANCH": m.Curr.Commit.Branch,
|
||||
"DRONE_COMMIT_LINK": m.Curr.Link,
|
||||
"DRONE_COMMIT_MESSAGE": m.Curr.Commit.Message,
|
||||
"DRONE_COMMIT_AUTHOR": m.Curr.Commit.Author.Name,
|
||||
"DRONE_COMMIT_AUTHOR_EMAIL": m.Curr.Commit.Author.Email,
|
||||
"DRONE_COMMIT_AUTHOR_AVATAR": m.Curr.Commit.Author.Avatar,
|
||||
"DRONE_BUILD_NUMBER": fmt.Sprintf("%d", m.Curr.Number),
|
||||
"DRONE_PARENT_BUILD_NUMBER": fmt.Sprintf("%d", m.Curr.Parent),
|
||||
"DRONE_BUILD_EVENT": m.Curr.Event,
|
||||
"DRONE_BUILD_LINK": fmt.Sprintf("%s/%s/%d", m.Sys.Link, m.Repo.Name, m.Curr.Number),
|
||||
"DRONE_BUILD_CREATED": fmt.Sprintf("%d", m.Curr.Created),
|
||||
"DRONE_BUILD_STARTED": fmt.Sprintf("%d", m.Curr.Started),
|
||||
"DRONE_BUILD_FINISHED": fmt.Sprintf("%d", m.Curr.Finished),
|
||||
"DRONE_JOB_NUMBER": fmt.Sprintf("%d", m.Job.Number),
|
||||
"DRONE_JOB_STARTED": fmt.Sprintf("%d", m.Curr.Started), // ISSUE: no job started
|
||||
"DRONE_BRANCH": m.Curr.Commit.Branch,
|
||||
"DRONE_COMMIT": m.Curr.Commit.Sha,
|
||||
"DRONE_VERSION": m.Sys.Version,
|
||||
"DRONE_DEPLOY_TO": m.Curr.Target,
|
||||
"DRONE_PREV_BUILD_STATUS": m.Prev.Status,
|
||||
"DRONE_PREV_BUILD_NUMBER": fmt.Sprintf("%v", m.Prev.Number),
|
||||
"DRONE_PREV_COMMIT_SHA": m.Prev.Commit.Sha,
|
||||
}
|
||||
if m.Curr.Event == EventTag {
|
||||
params["DRONE_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
|
||||
}
|
||||
if m.Curr.Event == EventPull {
|
||||
params["DRONE_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
var pullRegexp = regexp.MustCompile("\\d+")
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue