Switch to go modules, use forked go-curl to fix CI (#192)
This commit is contained in:
committed by
GitHub
parent
fcb627dccd
commit
ceb210f281
+1
-1
@@ -1,7 +1,7 @@
|
||||
# https://github.com/golangci/golangci/wiki/Configuration
|
||||
|
||||
service:
|
||||
project-path: github.com/Ullaakut/cameradar
|
||||
project-path: github.com/ullaakut/cameradar
|
||||
prepare:
|
||||
- apt-get update && apt-get install -y libcurl4-gnutls-dev
|
||||
- dep ensure
|
||||
|
||||
+2
-5
@@ -3,10 +3,10 @@ sudo: required
|
||||
language: go
|
||||
|
||||
env:
|
||||
- DEP_VERSION="0.5.0"
|
||||
- GO111MODULE=on
|
||||
|
||||
services:
|
||||
- docker
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- echo "Testing Docker Hub credentials"
|
||||
@@ -21,11 +21,8 @@ before_install:
|
||||
- sudo apt-get install -y docker-ce nmap
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker version
|
||||
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
|
||||
- chmod +x $GOPATH/bin/dep
|
||||
|
||||
install:
|
||||
- dep ensure
|
||||
- docker build -t cameradar .
|
||||
|
||||
script:
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ This file will give you guidelines on how to contribute if you want to, and will
|
||||
|
||||
If you're not into software development or not into Golang, you can still help. Updating the dictionaries for example, would be a really cool contribution! Just make sure the credentials and routes you add are **default constructor credentials** and not custom credentials.
|
||||
|
||||
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) or to directly [create an issue](https://github.com/Ullaakut/cameradar/issues)!
|
||||
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) or to directly [create an issue](https://github.com/ullaakut/cameradar/issues)!
|
||||
|
||||
## Version 2.0.0
|
||||
|
||||
@@ -67,7 +67,7 @@ Once everything is in order, we will merge your pull request.
|
||||
|
||||
Your code should just
|
||||
|
||||
- Not decrease the results of Cameradar on https://goreportcard.com/report/github.com/Ullaakut/cameradar
|
||||
- Not decrease the results of Cameradar on https://goreportcard.com/report/github.com/ullaakut/cameradar
|
||||
- Pass the code review
|
||||
|
||||
#### Golang
|
||||
@@ -77,6 +77,6 @@ Your code should just
|
||||
|
||||
## Contributors
|
||||
|
||||
- **Brendan Le Glaunec** - [@Ullaakut](https://github.com/Ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
|
||||
- **Brendan Le Glaunec** - [@Ullaakut](https://github.com/ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
|
||||
- **Jeremy Letang** - [@jeremyletang](https://github.com/jeremyletang) - letang.jeremy@gmail.com : *Idea of the project & Mentorship*
|
||||
- **ishanjain28** - [@ishanjain28](https://github.com/ishanjain28) - ishanjain28@gmail.com : *Implemented the environment variables support*
|
||||
- **ishanjain28** - [@ishanjain28](https://github.com/ishanjain28) - ishanjain28@gmail.com : *Implemented the environment variables support*
|
||||
|
||||
+6
-8
@@ -1,8 +1,8 @@
|
||||
# Build stage
|
||||
FROM golang:alpine AS build-env
|
||||
|
||||
COPY . /go/src/github.com/Ullaakut/cameradar
|
||||
WORKDIR /go/src/github.com/Ullaakut/cameradar/cameradar
|
||||
COPY . /go/src/github.com/ullaakut/cameradar
|
||||
WORKDIR /go/src/github.com/ullaakut/cameradar/cameradar
|
||||
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
@@ -12,10 +12,8 @@ RUN apk update && \
|
||||
libc-dev \
|
||||
git \
|
||||
pkgconfig
|
||||
ENV DEP_VERSION="0.5.0"
|
||||
RUN curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
|
||||
RUN chmod +x $GOPATH/bin/dep
|
||||
RUN dep ensure
|
||||
ENV GO111MODULE=on
|
||||
RUN go version
|
||||
RUN go build -o cameradar
|
||||
|
||||
# Final stage
|
||||
@@ -27,8 +25,8 @@ RUN apk --update add --no-cache nmap \
|
||||
curl-dev
|
||||
|
||||
WORKDIR /app/cameradar
|
||||
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/dictionaries/ /app/dictionaries/
|
||||
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cameradar/ /app/cameradar/
|
||||
COPY --from=build-env /go/src/github.com/ullaakut/cameradar/dictionaries/ /app/dictionaries/
|
||||
COPY --from=build-env /go/src/github.com/ullaakut/cameradar/cameradar/ /app/cameradar/
|
||||
|
||||
ENV CAMERADAR_CUSTOM_ROUTES="/app/dictionaries/routes"
|
||||
ENV CAMERADAR_CUSTOM_CREDENTIALS="/app/dictionaries/credentials.json"
|
||||
|
||||
Generated
-300
@@ -1,300 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:50b257fde8ffe647a6fdb6a7fb1314711232b7ecfb891f5cd5c140c046ab89cd"
|
||||
name = "github.com/Ullaakut/nmap"
|
||||
packages = [
|
||||
".",
|
||||
"pkg/osfamilies",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "6d3f7b465d7b37c8e019c95bc1c9f6c4f93e4ee7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e339fa18a6e1d4dd04282f5ba77b8a09cbbf1067ba20915de87d1e65367a9c9f"
|
||||
name = "github.com/andelf/go-curl"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9d81ad32de98e80df412c890c75d8964f696a910"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = "UT"
|
||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
|
||||
name = "github.com/fatih/color"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
|
||||
version = "v1.7.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:842de8d5a4c8fdbbceb55ab398dd8b68a35fe7f322012cf70571baa35c333ffa"
|
||||
name = "github.com/gernest/wow"
|
||||
packages = [
|
||||
".",
|
||||
"spin",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "7e0b2a2398989a5d220eebac5742d45422ba7de8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e1ff887e232b2d8f4f7c7db15a5fac7be418025afc4dda53c59c765dbb5aa6b4"
|
||||
name = "github.com/go-playground/locales"
|
||||
packages = [
|
||||
".",
|
||||
"currency",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f63010822830b6fe52288ee52d5a1151088ce039"
|
||||
version = "v0.12.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e022cf244bcac1b6ef933f1a2e0adcf6a6dfd7b872d8d41e4d4179bb09a87cbc"
|
||||
name = "github.com/go-playground/universal-translator"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "b32fa301c9fe55953584134cb6853a13c87ec0a1"
|
||||
version = "v0.16.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6782ffc812e8e700e6952ede1e60487ff1fd9da489eff762985be662a7cfc431"
|
||||
name = "github.com/leodido/go-urn"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "70078a794e8ea4b497ba7c19a78cd60f90ccf0f4"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2d0b44ee6208d256e29f55764db1fa41e3ae33fac6cef138acae6a79c3bd748e"
|
||||
name = "github.com/magefile/mage"
|
||||
packages = [
|
||||
"mg",
|
||||
"sh",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aedfce64c122eef47009b7f80c9771044753215d"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2353362d570a7bfa228149c62842019201cfb71"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
|
||||
version = "v0.0.4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
pruneopts = "UT"
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d707dbc1330c0ed177d4642d6ae102d5e2c847ebd0eb84562d0dc4f024531cfc"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "a5d6946387efe7d64d09dcba68cdd523dc1273a3"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:de37e343c64582d7026bf8ab6ac5b22a72eac54f3a57020db31524affed9f423"
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "6d33b5a963d922d182c91e8a1c88d81fd150cfd4"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
|
||||
name = "github.com/stretchr/objx"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:15a4a7e5afac3cea801fa24831fce3bf3b5bd3620cbf8355a07b7dbf06877883"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"mock",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
||||
version = "v1.2.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
pruneopts = "UT"
|
||||
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3d5e79e10549fd9119cbefd614b6d351ef5bd0be2f2b103a4199788e784cbc68"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b4a75ba826a64a70990f11a225237acd6ef35c9f"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8029e9743749d4be5bc9f7d42ea1659471767860f0cdc34d37c3111bd308a295"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"internal/gen",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"transform",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8abc978192c2fca4d6a74260c3a8f4aa34d25bd3c548c938745b91b8b20cda1d"
|
||||
name = "gopkg.in/go-playground/validator.v9"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "0277b12d53df79c9dbf7311cb07fa9c81ed621bb"
|
||||
version = "v9.24.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
|
||||
version = "v2.2.2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/Ullaakut/cameradar",
|
||||
"github.com/Ullaakut/nmap",
|
||||
"github.com/andelf/go-curl",
|
||||
"github.com/fatih/color",
|
||||
"github.com/gernest/wow",
|
||||
"github.com/gernest/wow/spin",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/pflag",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/mock",
|
||||
"gopkg.in/go-playground/validator.v9",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/andelf/go-curl"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fatih/color"
|
||||
version = "1.7.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/gernest/wow"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/pflag"
|
||||
version = "1.0.3"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/viper"
|
||||
version = "1.3.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.2"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
@@ -17,17 +17,17 @@
|
||||
<a href='https://coveralls.io/github/Ullaakut/cameradar?branch=master'>
|
||||
<img src='https://coveralls.io/repos/github/Ullaakut/cameradar/badge.svg?branch=master' alt='Coverage Status' />
|
||||
</a>
|
||||
<a href="https://golangci.com/r/github.com/Ullaakut/cameradar">
|
||||
<img src="https://golangci.com/badges/github.com/Ullaakut/cameradar.svg" />
|
||||
<a href="https://golangci.com/r/github.com/ullaakut/cameradar">
|
||||
<img src="https://golangci.com/badges/github.com/ullaakut/cameradar.svg" />
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/Ullaakut/cameradar">
|
||||
<img src="https://goreportcard.com/badge/github.com/Ullaakut/cameradar" />
|
||||
<a href="https://goreportcard.com/report/github.com/ullaakut/cameradar">
|
||||
<img src="https://goreportcard.com/badge/github.com/ullaakut/cameradar" />
|
||||
</a>
|
||||
<a href="https://github.com/Ullaakut/cameradar/releases/latest">
|
||||
<a href="https://github.com/ullaakut/cameradar/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/Ullaakut/cameradar.svg?style=flat" />
|
||||
</a>
|
||||
<a href="https://godoc.org/github.com/Ullaakut/cameradar">
|
||||
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" />
|
||||
<a href="https://godoc.org/github.com/ullaakut/cameradar">
|
||||
<img src="https://godoc.org/github.com/ullaakut/cameradar?status.svg" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -88,8 +88,8 @@ Only use this solution if for some reason using docker is not an option for you
|
||||
|
||||
Make sure you installed the dependencies mentionned above.
|
||||
|
||||
1. `go get github.com/Ullaakut/cameradar`
|
||||
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
|
||||
1. `go get github.com/ullaakut/cameradar`
|
||||
2. `cd $GOPATH/src/github.com/ullaakut/cameradar`
|
||||
3. `dep ensure`
|
||||
4. `cd cameradar`
|
||||
5. `go install`
|
||||
@@ -104,15 +104,15 @@ The `cameradar` binary is now in your `$GOPATH/bin` ready to be used. See comman
|
||||
* `github.com/ullaakut/nmap`
|
||||
* `github.com/pkg/errors`
|
||||
* `gopkg.in/go-playground/validator.v9`
|
||||
* `github.com/andelf/go-curl`
|
||||
* `github.com/ullaakut/go-curl`
|
||||
|
||||
#### Installing the library
|
||||
|
||||
`go get github.com/Ullaakut/cameradar`
|
||||
`go get github.com/ullaakut/cameradar`
|
||||
|
||||
After this command, the _cameradar_ library is ready to use. Its source will be in:
|
||||
|
||||
$GOPATH/src/pkg/github.com/Ullaakut/cameradar
|
||||
$GOPATH/src/pkg/github.com/ullaakut/cameradar
|
||||
|
||||
You can use `go get -u` to update the package.
|
||||
|
||||
@@ -284,7 +284,7 @@ You can still find it under the 1.1.4 tag on this repo, however it was less perf
|
||||
|
||||
> How to use the Cameradar library for my own project?
|
||||
|
||||
See the example in `/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/Ullaakut/cameradar).
|
||||
See the example in `/cameradar`. You just need to run `go get github.com/ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/ullaakut/cameradar).
|
||||
|
||||
> I want to scan my own localhost for some reason and it does not work! What's going on?
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
curl "github.com/andelf/go-curl"
|
||||
curl "github.com/ullaakut/go-curl"
|
||||
"github.com/pkg/errors"
|
||||
v "gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
curl "github.com/andelf/go-curl"
|
||||
curl "github.com/ullaakut/go-curl"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ullaakut/cameradar"
|
||||
|
||||
curl "github.com/andelf/go-curl"
|
||||
"github.com/fatih/color"
|
||||
"github.com/gernest/wow"
|
||||
"github.com/gernest/wow/spin"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
cmrdr "github.com/ullaakut/cameradar"
|
||||
curl "github.com/ullaakut/go-curl"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
@@ -33,8 +32,8 @@ func parseArguments() error {
|
||||
|
||||
pflag.StringSliceP("targets", "t", []string{}, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
|
||||
pflag.StringSliceP("ports", "p", []string{"554", "5554", "8554"}, "The ports on which to search for RTSP streams")
|
||||
pflag.StringP("custom-routes", "r", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
|
||||
pflag.StringP("custom-credentials", "c", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json", "The path on which to load a custom credentials JSON dictionary")
|
||||
pflag.StringP("custom-routes", "r", "<GOPATH>/src/github.com/ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
|
||||
pflag.StringP("custom-credentials", "c", "<GOPATH>/src/github.com/ullaakut/cameradar/dictionaries/credentials.json", "The path on which to load a custom credentials JSON dictionary")
|
||||
pflag.IntP("speed", "s", 4, "The nmap speed preset to use for discovery")
|
||||
pflag.IntP("timeout", "T", 2000, "The timeout in miliseconds to use for attack attempts")
|
||||
pflag.BoolP("log", "l", false, "Enable the logs for nmap's output to stdout")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// IP Cameras, often for surveillance.
|
||||
//
|
||||
// A simple example usage of the library can be found in
|
||||
// https://github.com/Ullaakut/cameradar/tree/master/cameradar
|
||||
// https://github.com/ullaakut/cameradar/tree/master/cameradar
|
||||
//
|
||||
// The example usage is complete enough for most users to
|
||||
// ignore the library, but for users with specific needs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cmrdr
|
||||
|
||||
import (
|
||||
curl "github.com/andelf/go-curl"
|
||||
curl "github.com/ullaakut/go-curl"
|
||||
)
|
||||
|
||||
// Curler is an interface that implements the CURL interface of the go-curl library
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
curl "github.com/andelf/go-curl"
|
||||
curl "github.com/ullaakut/go-curl"
|
||||
)
|
||||
|
||||
func TestCurl(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package cmrdr
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Ullaakut/nmap"
|
||||
"github.com/ullaakut/nmap"
|
||||
)
|
||||
|
||||
// Discover scans the target networks and tries to find RTSP streams within them.
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/Ullaakut/nmap"
|
||||
"github.com/ullaakut/nmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
module github.com/ullaakut/cameradar
|
||||
|
||||
require (
|
||||
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead // indirect
|
||||
github.com/fatih/color v1.7.0 // indirect
|
||||
github.com/gernest/wow v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.12.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||
github.com/leodido/go-urn v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.6 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/spf13/viper v1.3.1 // indirect
|
||||
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70
|
||||
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead
|
||||
gopkg.in/go-playground/validator.v9 v9.27.0
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead h1:iclmd4In7CnuZGbbnnaeF1DtSePgXxN71pq5UNI1M7c=
|
||||
github.com/Ullaakut/nmap v0.0.0-20190306183004-e38898a9bead/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gernest/wow v0.1.0 h1:g9xdwCwP0+xgVYlA2sopI0gZHqXe7HjI/7/LykG4fks=
|
||||
github.com/gernest/wow v0.1.0/go.mod h1:dEPabJRi5BneI1Nev1VWo0ZlcTWibHWp43qxKms4elY=
|
||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
|
||||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38=
|
||||
github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70 h1:3q4hgRu9NT894aYmnoMFl5wPvdNhpHYmdi2+Njyxq5U=
|
||||
github.com/ullaakut/go-curl v0.0.0-20190310175419-50acab4cef70/go.mod h1:FTfXm4jC9Ff1yqc3/HMXCyr+SGO03vJyijJCQlNyF10=
|
||||
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead h1:Pw5wKSAfxi8GcYJSc3GdcwtPG5tyg7zg9E3hAHbLPO0=
|
||||
github.com/ullaakut/nmap v0.0.0-20190306183004-e38898a9bead/go.mod h1:4CQy4PqZA4Snk3+MS26+1oAkJ8dCY8kGH6+kF42yajw=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/validator.v9 v9.27.0 h1:wCg/0hk9RzcB0CYw8pYV6FiBYug1on0cpco9YZF8jqA=
|
||||
gopkg.in/go-playground/validator.v9 v9.27.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
## Golang
|
||||
|
||||
### Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
### Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
### Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
## MacOS
|
||||
|
||||
### General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
### Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
### Thumbnails
|
||||
._*
|
||||
|
||||
### Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
### Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
## IDEs
|
||||
|
||||
### VSCode
|
||||
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
### JetBrains
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 1m
|
||||
|
||||
tests: false
|
||||
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs:
|
||||
- pkg/osfamilies
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
ignore: fmt:.*,io/ioutil:^Read.*,os/exec:^Kill.*
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
min-confidence: 0.8
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/org/project
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 10
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 150
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
min-occurrences: 3
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/davecgh/go-spew/spew
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
locale: US
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
unparam:
|
||||
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
|
||||
# and rta for programs with main packages. Default is cha.
|
||||
algo: cha
|
||||
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
max-func-lines: 30
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
simple: true
|
||||
range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
gocritic:
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- performance
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
enable-all: false
|
||||
disable:
|
||||
- maligned
|
||||
- prealloc
|
||||
disable-all: false
|
||||
presets:
|
||||
- bugs
|
||||
- unused
|
||||
fast: false
|
||||
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
- "Subprocess launching should be audited"
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
new: false
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
dist: trusty
|
||||
sudo: required
|
||||
language: go
|
||||
|
||||
before_install:
|
||||
- sudo apt-get install -y nmap
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
# Run unit tests
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
|
||||
# Ensure the examples compile
|
||||
- for dir in examples/*/; do go build -o $dir/bin $dir/main.go; done
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- brendan.le-glaunec@epitech.eu
|
||||
on_success: never
|
||||
on_failure: always
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Ullaakut
|
||||
|
||||
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.
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
# nmap
|
||||
|
||||
<p align="center">
|
||||
<img width="350" src="img/logo.png"/>
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" />
|
||||
</a>
|
||||
<a href="https://godoc.org/github.com/Ullaakut/nmap">
|
||||
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" />
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/github.com/ullaakut/nmap">
|
||||
<img src="https://goreportcard.com/badge/github.com/ullaakut/nmap">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/Ullaakut/nmap">
|
||||
<img src="https://travis-ci.org/Ullaakut/nmap.svg?branch=master">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/Ullaakut/nmap?branch=master">
|
||||
<img src="https://coveralls.io/repos/github/Ullaakut/nmap/badge.svg?branch=master">
|
||||
</a>
|
||||
<p>
|
||||
|
||||
This library aims at providing idiomatic `nmap` bindings for go developers, in order to make it easier to write security audit tools using golang.
|
||||
|
||||
<!-- It allows not only to parse the XML output of nmap, but also to get the output of nmap as it is running, through a channel. This can be useful for computing a scan's progress, or simply displaying live information to your users. -->
|
||||
|
||||
## It's currently a work in progress
|
||||
|
||||
This paragraph won't be removed until the library is ready to be used and properly documented.
|
||||
|
||||
## Supported features
|
||||
|
||||
- [x] All of `nmap`'s options as `WithXXX` methods.
|
||||
- [x] Cancellable contexts support.
|
||||
- [x] [Idiomatic go filters](examples/service_detection/main.go#L19).
|
||||
- [x] Helpful enums for most nmap commands. (time templates, os families, port states, etc.)
|
||||
- [x] Complete documentation of each option, mostly insipred from nmap's documentation.
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Examples of usage - Work in progress (4/7 examples so far)
|
||||
- [ ] Complete unit tests - Work in progress (95% coverage so far)
|
||||
- [ ] Asynchronous scan
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/Ullaakut/nmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
|
||||
// with a 5 minute timeout.
|
||||
scanner, err := nmap.NewScanner(
|
||||
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
|
||||
nmap.WithPorts("80,443,843"),
|
||||
nmap.WithContext(ctx),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create nmap scanner: %v", err)
|
||||
}
|
||||
|
||||
result, err := scanner.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to run nmap scan: %v", err)
|
||||
}
|
||||
|
||||
// Use the results to print an example output
|
||||
for _, host := range result.Hosts {
|
||||
if len(host.Ports) == 0 || len(host.Addresses) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("Host %q:\n", host.Addresses[0])
|
||||
|
||||
for _, port := range host.Ports {
|
||||
fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Nmap done: %d hosts up scanned in %3f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
|
||||
}
|
||||
```
|
||||
|
||||
The program above outputs:
|
||||
|
||||
```bash
|
||||
Host "172.217.16.46":
|
||||
Port 80/tcp open http
|
||||
Port 443/tcp open https
|
||||
Port 843/tcp filtered unknown
|
||||
Host "31.13.81.36":
|
||||
Port 80/tcp open http
|
||||
Port 443/tcp open https
|
||||
Port 843/tcp open unknown
|
||||
Host "216.58.215.110":
|
||||
Port 80/tcp open http
|
||||
Port 443/tcp open https
|
||||
Port 843/tcp filtered unknown
|
||||
Nmap done: 3 hosts up scanned in 1.29 seconds
|
||||
```
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package nmap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNmapNotInstalled means that upon trying to manually locate nmap in the user's path,
|
||||
// it was not found. Either use the WithBinaryPath method to set it manually, or make sure that
|
||||
// the nmap binary is present in the user's $PATH.
|
||||
ErrNmapNotInstalled = errors.New("'nmap' binary was not found")
|
||||
|
||||
// ErrScanTimeout means that the provided context was done before the scanner finished its scan.
|
||||
ErrScanTimeout = errors.New("nmap scan timed out")
|
||||
|
||||
// ErrNoTargetsSpecified means that no targets were specified.
|
||||
ErrNoTargetsSpecified = errors.New("no targets specified")
|
||||
)
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package nmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// A scanner can be instanciated with options to set the arguments
|
||||
// that are given to nmap.
|
||||
func ExampleScanner_simple() {
|
||||
s, err := NewScanner(
|
||||
WithTargets("google.com", "facebook.com", "youtube.com"),
|
||||
WithCustomDNSServers("8.8.8.8", "8.8.4.4"),
|
||||
WithTimingTemplate(TimingFastest),
|
||||
WithTCPScanFlags(FlagACK, FlagNULL, FlagRST),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create nmap scanner: %v", err)
|
||||
}
|
||||
|
||||
scanResult, err := s.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("nmap encountered an error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"Scan successful: %d hosts up\n",
|
||||
scanResult.Stats.Hosts.Up,
|
||||
)
|
||||
// Output: Scan successful: 3 hosts up
|
||||
}
|
||||
|
||||
// A scanner can be given custom idiomatic filters for both hosts
|
||||
// and ports.
|
||||
func ExampleScanner_filters() {
|
||||
s, err := NewScanner(
|
||||
WithTargets("google.com", "facebook.com"),
|
||||
WithPorts("843"),
|
||||
WithFilterHost(func(h Host) bool {
|
||||
// Filter out hosts with no open ports.
|
||||
for idx := range h.Ports {
|
||||
if h.Ports[idx].Status() == "open" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create nmap scanner: %v", err)
|
||||
}
|
||||
|
||||
scanResult, err := s.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("nmap encountered an error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"Filtered out hosts %d / Original number of hosts: %d\n",
|
||||
len(scanResult.Hosts),
|
||||
scanResult.Stats.Hosts.Total,
|
||||
)
|
||||
// Output: Filtered out hosts 1 / Original number of hosts: 2
|
||||
}
|
||||
-1216
File diff suppressed because it is too large
Load Diff
-1781
File diff suppressed because it is too large
Load Diff
-644
@@ -1,644 +0,0 @@
|
||||
package osfamilies
|
||||
|
||||
// OSFamily describes an OS Family, usually from a constructor or
|
||||
// a kernel.
|
||||
type OSFamily string
|
||||
|
||||
// OSFamily definitions.
|
||||
const (
|
||||
TwoN OSFamily = "2N"
|
||||
TwoWire OSFamily = "2Wire"
|
||||
ThreeCom OSFamily = "3Com"
|
||||
ThreeM OSFamily = "3M"
|
||||
FourG OSFamily = "4G"
|
||||
ATec OSFamily = "A-Tec"
|
||||
ADIC OSFamily = "ADIC"
|
||||
AKCP OSFamily = "AKCP"
|
||||
AMX OSFamily = "AMX"
|
||||
APC OSFamily = "APC"
|
||||
ARCA OSFamily = "ARCA"
|
||||
ATandT OSFamily = "AT&T"
|
||||
AVM OSFamily = "AVM"
|
||||
AVtech OSFamily = "AVtech"
|
||||
AXIS OSFamily = "AXIS"
|
||||
Aastra OSFamily = "Aastra"
|
||||
AcBel OSFamily = "AcBel"
|
||||
Aceex OSFamily = "Aceex"
|
||||
Acer OSFamily = "Acer"
|
||||
Acme OSFamily = "Acme"
|
||||
Acorp OSFamily = "Acorp"
|
||||
Actiontec OSFamily = "Actiontec"
|
||||
Adaptec OSFamily = "Adaptec"
|
||||
Adtran OSFamily = "Adtran"
|
||||
Adva OSFamily = "Adva"
|
||||
Advanced OSFamily = "Advanced"
|
||||
Aerohive OSFamily = "Aerohive"
|
||||
Aethra OSFamily = "Aethra"
|
||||
Agfa OSFamily = "Agfa"
|
||||
AirLive OSFamily = "AirLive"
|
||||
AirMagnet OSFamily = "AirMagnet"
|
||||
AirSpan OSFamily = "AirSpan"
|
||||
Airaya OSFamily = "Airaya"
|
||||
Airlink101 OSFamily = "Airlink101"
|
||||
Airnet OSFamily = "Airnet"
|
||||
Airvana OSFamily = "Airvana"
|
||||
Alaxala OSFamily = "Alaxala"
|
||||
Alcatel OSFamily = "Alcatel"
|
||||
AlcatelLucent OSFamily = "Alcatel-Lucent"
|
||||
Alice OSFamily = "Alice"
|
||||
AllenBradley OSFamily = "Allen-Bradley"
|
||||
Allied OSFamily = "Allied"
|
||||
Allnet OSFamily = "Allnet"
|
||||
Allworx OSFamily = "Allworx"
|
||||
Alvarion OSFamily = "Alvarion"
|
||||
Amazon OSFamily = "Amazon"
|
||||
Ambit OSFamily = "Ambit"
|
||||
Amiga OSFamily = "Amiga"
|
||||
Anue OSFamily = "Anue"
|
||||
Apple OSFamily = "Apple"
|
||||
Arcor OSFamily = "Arcor"
|
||||
Areca OSFamily = "Areca"
|
||||
Argon OSFamily = "Argon"
|
||||
Argosy OSFamily = "Argosy"
|
||||
Arris OSFamily = "Arris"
|
||||
Aruba OSFamily = "Aruba"
|
||||
Asmax OSFamily = "Asmax"
|
||||
Asus OSFamily = "Asus"
|
||||
Atari OSFamily = "Atari"
|
||||
Atcom OSFamily = "Atcom"
|
||||
AudioCodes OSFamily = "AudioCodes"
|
||||
AudioControl OSFamily = "AudioControl"
|
||||
Avaya OSFamily = "Avaya"
|
||||
Avocent OSFamily = "Avocent"
|
||||
Axcient OSFamily = "Axcient"
|
||||
AzBox OSFamily = "AzBox"
|
||||
BECK OSFamily = "BECK"
|
||||
BSD OSFamily = "BSD"
|
||||
BSDI OSFamily = "BSDI"
|
||||
BT OSFamily = "BT"
|
||||
Barracuda OSFamily = "Barracuda"
|
||||
Barrelfish OSFamily = "Barrelfish"
|
||||
Basler OSFamily = "Basler"
|
||||
Bay OSFamily = "Bay"
|
||||
BeaconMedaes OSFamily = "BeaconMedaes"
|
||||
Beat OSFamily = "Beat"
|
||||
Belkin OSFamily = "Belkin"
|
||||
Bell OSFamily = "Bell"
|
||||
Billion OSFamily = "Billion"
|
||||
BinTec OSFamily = "BinTec"
|
||||
BlackBox OSFamily = "BlackBox"
|
||||
Blackboard OSFamily = "Blackboard"
|
||||
Blue OSFamily = "Blue"
|
||||
BlueArc OSFamily = "BlueArc"
|
||||
Bluebird OSFamily = "Bluebird"
|
||||
Bomara OSFamily = "Bomara"
|
||||
Bosch OSFamily = "Bosch"
|
||||
Bose OSFamily = "Bose"
|
||||
Boundless OSFamily = "Boundless"
|
||||
Bowers OSFamily = "Bowers"
|
||||
British OSFamily = "British"
|
||||
BroadMax OSFamily = "BroadMax"
|
||||
Brocade OSFamily = "Brocade"
|
||||
Brother OSFamily = "Brother"
|
||||
Buffalo OSFamily = "Buffalo"
|
||||
Burny OSFamily = "Burny"
|
||||
Bush OSFamily = "Bush"
|
||||
CNav OSFamily = "C-Nav"
|
||||
CAEN OSFamily = "CAEN"
|
||||
CMI OSFamily = "CMI"
|
||||
Cabletron OSFamily = "Cabletron"
|
||||
Caldera OSFamily = "Caldera"
|
||||
Calix OSFamily = "Calix"
|
||||
Cameo OSFamily = "Cameo"
|
||||
Canon OSFamily = "Canon"
|
||||
Casio OSFamily = "Casio"
|
||||
Cayman OSFamily = "Cayman"
|
||||
Ceedtec OSFamily = "Ceedtec"
|
||||
Check OSFamily = "Check"
|
||||
Chip OSFamily = "Chip"
|
||||
CipherLab OSFamily = "CipherLab"
|
||||
Cisco OSFamily = "Cisco"
|
||||
Citrix OSFamily = "Citrix"
|
||||
CoRAID OSFamily = "CoRAID"
|
||||
Cobalt OSFamily = "Cobalt"
|
||||
Cognex OSFamily = "Cognex"
|
||||
Comau OSFamily = "Comau"
|
||||
Compal OSFamily = "Compal"
|
||||
Compaq OSFamily = "Compaq"
|
||||
Comtrend OSFamily = "Comtrend"
|
||||
Conceptronic OSFamily = "Conceptronic"
|
||||
Control4 OSFamily = "Control4"
|
||||
Coyote OSFamily = "Coyote"
|
||||
Cray OSFamily = "Cray"
|
||||
Crestron OSFamily = "Crestron"
|
||||
CyanogenMod OSFamily = "CyanogenMod"
|
||||
Cyberoam OSFamily = "Cyberoam"
|
||||
Cymphonix OSFamily = "Cymphonix"
|
||||
DLink OSFamily = "D-Link"
|
||||
DEC OSFamily = "DEC"
|
||||
DMP OSFamily = "DMP"
|
||||
DTE OSFamily = "DTE"
|
||||
DVTel OSFamily = "DVTel"
|
||||
DYMO OSFamily = "DYMO"
|
||||
Data OSFamily = "Data"
|
||||
Datalogic OSFamily = "Datalogic"
|
||||
Daysequerra OSFamily = "Daysequerra"
|
||||
Decru OSFamily = "Decru"
|
||||
Dedicated OSFamily = "Dedicated"
|
||||
Dell OSFamily = "Dell"
|
||||
Denon OSFamily = "Denon"
|
||||
Denver OSFamily = "Denver"
|
||||
Develop OSFamily = "Develop"
|
||||
Dick OSFamily = "Dick"
|
||||
Digi OSFamily = "Digi"
|
||||
Digital OSFamily = "Digital"
|
||||
Digitus OSFamily = "Digitus"
|
||||
Digium OSFamily = "Digium"
|
||||
DirecTV OSFamily = "DirecTV"
|
||||
Dish OSFamily = "Dish"
|
||||
Dolby OSFamily = "Dolby"
|
||||
DragonFly OSFamily = "DragonFly"
|
||||
DragonWave OSFamily = "DragonWave"
|
||||
DrayTek OSFamily = "DrayTek"
|
||||
Draytek OSFamily = "Draytek"
|
||||
Drayton OSFamily = "Drayton"
|
||||
Dream OSFamily = "Dream"
|
||||
Drobo OSFamily = "Drobo"
|
||||
EMC OSFamily = "EMC"
|
||||
ESI OSFamily = "ESI"
|
||||
ETH OSFamily = "ETH"
|
||||
EasyPath OSFamily = "EasyPath"
|
||||
Eaton OSFamily = "Eaton"
|
||||
Efficient OSFamily = "Efficient"
|
||||
Eicon OSFamily = "Eicon"
|
||||
Elfiq OSFamily = "Elfiq"
|
||||
Elk OSFamily = "Elk"
|
||||
Elsag OSFamily = "Elsag"
|
||||
Ember OSFamily = "Ember"
|
||||
Emerson OSFamily = "Emerson"
|
||||
EnGenius OSFamily = "EnGenius"
|
||||
Encore OSFamily = "Encore"
|
||||
Endian OSFamily = "Endian"
|
||||
Enerdis OSFamily = "Enerdis"
|
||||
Engetron OSFamily = "Engetron"
|
||||
Enistic OSFamily = "Enistic"
|
||||
Enlogic OSFamily = "Enlogic"
|
||||
Enterasys OSFamily = "Enterasys"
|
||||
Epson OSFamily = "Epson"
|
||||
Ericsson OSFamily = "Ericsson"
|
||||
Espressif OSFamily = "Espressif"
|
||||
Essentia OSFamily = "Essentia"
|
||||
EtherWerX OSFamily = "EtherWerX"
|
||||
Exabyte OSFamily = "Exabyte"
|
||||
Excito OSFamily = "Excito"
|
||||
Express OSFamily = "Express"
|
||||
Exterity OSFamily = "Exterity"
|
||||
Extreme OSFamily = "Extreme"
|
||||
F5 OSFamily = "F5"
|
||||
FORE OSFamily = "FORE"
|
||||
Fatek OSFamily = "Fatek"
|
||||
FireBrick OSFamily = "FireBrick"
|
||||
Force10 OSFamily = "Force10"
|
||||
Fortinet OSFamily = "Fortinet"
|
||||
Foscam OSFamily = "Foscam"
|
||||
Foundry OSFamily = "Foundry"
|
||||
Free OSFamily = "Free"
|
||||
FreeBSD OSFamily = "FreeBSD"
|
||||
FreeNAS OSFamily = "FreeNAS"
|
||||
Freecom OSFamily = "Freecom"
|
||||
Fronius OSFamily = "Fronius"
|
||||
Frontier OSFamily = "Frontier"
|
||||
Fuji OSFamily = "Fuji"
|
||||
Fujian OSFamily = "Fujian"
|
||||
Fujitsu OSFamily = "Fujitsu"
|
||||
Funkwerk OSFamily = "Funkwerk"
|
||||
GNU OSFamily = "GNU"
|
||||
GalaxyMetalGear OSFamily = "GalaxyMetalGear"
|
||||
Gargoyle OSFamily = "Gargoyle"
|
||||
Garmin OSFamily = "Garmin"
|
||||
GbE2c OSFamily = "GbE2c"
|
||||
Geist OSFamily = "Geist"
|
||||
Gemtek OSFamily = "Gemtek"
|
||||
General OSFamily = "General"
|
||||
Generex OSFamily = "Generex"
|
||||
Gennet OSFamily = "Gennet"
|
||||
Genua OSFamily = "Genua"
|
||||
George OSFamily = "George"
|
||||
Geovision OSFamily = "Geovision"
|
||||
GlobespanVirata OSFamily = "GlobespanVirata"
|
||||
GoPro OSFamily = "GoPro"
|
||||
Google OSFamily = "Google"
|
||||
Grace OSFamily = "Grace"
|
||||
Grandstream OSFamily = "Grandstream"
|
||||
Green OSFamily = "Green"
|
||||
H3C OSFamily = "H3C"
|
||||
HID OSFamily = "HID"
|
||||
HP OSFamily = "HP"
|
||||
HW OSFamily = "HW"
|
||||
Haiku OSFamily = "Haiku"
|
||||
Hamlet OSFamily = "Hamlet"
|
||||
Harris OSFamily = "Harris"
|
||||
Hawking OSFamily = "Hawking"
|
||||
Hay OSFamily = "Hay"
|
||||
Head OSFamily = "Head"
|
||||
Henry OSFamily = "Henry"
|
||||
HighFlying OSFamily = "High-Flying"
|
||||
Hikvision OSFamily = "Hikvision"
|
||||
Hioki OSFamily = "Hioki"
|
||||
Hirschmann OSFamily = "Hirschmann"
|
||||
Hitron OSFamily = "Hitron"
|
||||
Hotway OSFamily = "Hotway"
|
||||
Huawei OSFamily = "Huawei"
|
||||
Hybertone OSFamily = "Hybertone"
|
||||
IBM OSFamily = "IBM"
|
||||
IEI OSFamily = "IEI"
|
||||
IGEL OSFamily = "IGEL"
|
||||
IHome OSFamily = "IHome"
|
||||
IOData OSFamily = "IO-Data"
|
||||
IOGear OSFamily = "IOGear"
|
||||
ION OSFamily = "ION"
|
||||
IPAD OSFamily = "IPAD"
|
||||
IPCop OSFamily = "IPCop"
|
||||
IPFire OSFamily = "IPFire"
|
||||
ISS OSFamily = "ISS"
|
||||
ITW OSFamily = "ITW"
|
||||
Icom OSFamily = "Icom"
|
||||
Icy OSFamily = "Icy"
|
||||
Imperva OSFamily = "Imperva"
|
||||
Infoblox OSFamily = "Infoblox"
|
||||
Infomir OSFamily = "Infomir"
|
||||
Infrant OSFamily = "Infrant"
|
||||
Inova OSFamily = "Inova"
|
||||
Instar OSFamily = "Instar"
|
||||
Intel OSFamily = "Intel"
|
||||
Interbell OSFamily = "Interbell"
|
||||
Interflex OSFamily = "Interflex"
|
||||
Intermec OSFamily = "Intermec"
|
||||
Interpeak OSFamily = "Interpeak"
|
||||
Intertex OSFamily = "Intertex"
|
||||
Intracom OSFamily = "Intracom"
|
||||
Inventel OSFamily = "Inventel"
|
||||
Iomega OSFamily = "Iomega"
|
||||
IronPort OSFamily = "IronPort"
|
||||
Isilon OSFamily = "Isilon"
|
||||
Iskratel OSFamily = "Iskratel"
|
||||
JTEKT OSFamily = "JTEKT"
|
||||
Joyent OSFamily = "Joyent"
|
||||
Juniper OSFamily = "Juniper"
|
||||
KA9Q OSFamily = "KA9Q"
|
||||
KCorp OSFamily = "KCorp"
|
||||
KWSoftware OSFamily = "KW-Software"
|
||||
KabaBenzing OSFamily = "Kaba-Benzing"
|
||||
Kaiomy OSFamily = "Kaiomy"
|
||||
Kapsch OSFamily = "Kapsch"
|
||||
Kartina OSFamily = "Kartina"
|
||||
Kemp OSFamily = "Kemp"
|
||||
Keyence OSFamily = "Keyence"
|
||||
Kodak OSFamily = "Kodak"
|
||||
Kongsberg OSFamily = "Kongsberg"
|
||||
Konica OSFamily = "Konica"
|
||||
Koukaam OSFamily = "Koukaam"
|
||||
Kronos OSFamily = "Kronos"
|
||||
Kyocera OSFamily = "Kyocera"
|
||||
LG OSFamily = "LG"
|
||||
LaCie OSFamily = "LaCie"
|
||||
LaCrosse OSFamily = "LaCrosse"
|
||||
LaSAT OSFamily = "LaSAT"
|
||||
Lancom OSFamily = "Lancom"
|
||||
Lanier OSFamily = "Lanier"
|
||||
Lantronix OSFamily = "Lantronix"
|
||||
Larus OSFamily = "Larus"
|
||||
Leica OSFamily = "Leica"
|
||||
Lenel OSFamily = "Lenel"
|
||||
Leolink OSFamily = "Leolink"
|
||||
LevelOne OSFamily = "LevelOne"
|
||||
Lexmark OSFamily = "Lexmark"
|
||||
Liebert OSFamily = "Liebert"
|
||||
LifeSize OSFamily = "LifeSize"
|
||||
Linksys OSFamily = "Linksys"
|
||||
Linux OSFamily = "Linux"
|
||||
LogiLink OSFamily = "LogiLink"
|
||||
Logitech OSFamily = "Logitech"
|
||||
Lorex OSFamily = "Lorex"
|
||||
Lucent OSFamily = "Lucent"
|
||||
Luminary OSFamily = "Luminary"
|
||||
Luxul OSFamily = "Luxul"
|
||||
Lyngsoe OSFamily = "Lyngsoe"
|
||||
MGE OSFamily = "MGE"
|
||||
MOXA OSFamily = "MOXA"
|
||||
MPI OSFamily = "MPI"
|
||||
Macsense OSFamily = "Macsense"
|
||||
Maipu OSFamily = "Maipu"
|
||||
Mapower OSFamily = "Mapower"
|
||||
Marantz OSFamily = "Marantz"
|
||||
McAfee OSFamily = "McAfee"
|
||||
Meinberg OSFamily = "Meinberg"
|
||||
Meru OSFamily = "Meru"
|
||||
Metrix OSFamily = "Metrix"
|
||||
MicroNet OSFamily = "MicroNet"
|
||||
Microsoft OSFamily = "Microsoft"
|
||||
Microware OSFamily = "Microware"
|
||||
MikroTik OSFamily = "MikroTik"
|
||||
Milight OSFamily = "Milight"
|
||||
Minix OSFamily = "Minix"
|
||||
Minolta OSFamily = "Minolta"
|
||||
Mirapoint OSFamily = "Mirapoint"
|
||||
Mitel OSFamily = "Mitel"
|
||||
Mitrastar OSFamily = "Mitrastar"
|
||||
Mitsubishi OSFamily = "Mitsubishi"
|
||||
Modtronix OSFamily = "Modtronix"
|
||||
Motorola OSFamily = "Motorola"
|
||||
MusicianLink OSFamily = "MusicianLink"
|
||||
NCR OSFamily = "NCR"
|
||||
NEC OSFamily = "NEC"
|
||||
NOXON OSFamily = "NOXON"
|
||||
NRG OSFamily = "NRG"
|
||||
NSFOCUS OSFamily = "NSFOCUS"
|
||||
NTI OSFamily = "NTI"
|
||||
NTT OSFamily = "NTT"
|
||||
Nashuatec OSFamily = "Nashuatec"
|
||||
National OSFamily = "National"
|
||||
NeXT OSFamily = "NeXT"
|
||||
Neopost OSFamily = "Neopost"
|
||||
Ness OSFamily = "Ness"
|
||||
Nest OSFamily = "Nest"
|
||||
NetApp OSFamily = "NetApp"
|
||||
NetBSD OSFamily = "NetBSD"
|
||||
NetBurner OSFamily = "NetBurner"
|
||||
NetOptics OSFamily = "NetOptics"
|
||||
Netasq OSFamily = "Netasq"
|
||||
Netcomm OSFamily = "Netcomm"
|
||||
Netgear OSFamily = "Netgear"
|
||||
Netgem OSFamily = "Netgem"
|
||||
Netopia OSFamily = "Netopia"
|
||||
Network OSFamily = "Network"
|
||||
NetworkAlchemy OSFamily = "NetworkAlchemy"
|
||||
NetworksAOK OSFamily = "NetworksAOK"
|
||||
Neuf OSFamily = "Neuf"
|
||||
Newave OSFamily = "Newave"
|
||||
NexStor OSFamily = "NexStor"
|
||||
Nexenta OSFamily = "Nexenta"
|
||||
Nexsan OSFamily = "Nexsan"
|
||||
Nibe OSFamily = "Nibe"
|
||||
Nintendo OSFamily = "Nintendo"
|
||||
NodeMCU OSFamily = "NodeMCU"
|
||||
Nokia OSFamily = "Nokia"
|
||||
Nomadix OSFamily = "Nomadix"
|
||||
Nortel OSFamily = "Nortel"
|
||||
Novatel OSFamily = "Novatel"
|
||||
Novell OSFamily = "Novell"
|
||||
NutOS OSFamily = "Nut/OS"
|
||||
OSRAM OSFamily = "OSRAM"
|
||||
Obihai OSFamily = "Obihai"
|
||||
Ocean OSFamily = "Ocean"
|
||||
Oki OSFamily = "Oki"
|
||||
Olivetti OSFamily = "Olivetti"
|
||||
Olympus OSFamily = "Olympus"
|
||||
Omron OSFamily = "Omron"
|
||||
On OSFamily = "On"
|
||||
OnStor OSFamily = "OnStor"
|
||||
Onboard OSFamily = "Onboard"
|
||||
OneAccess OSFamily = "OneAccess"
|
||||
OpenBSD OSFamily = "OpenBSD"
|
||||
OpenBox OSFamily = "OpenBox"
|
||||
Opto OSFamily = "Opto"
|
||||
Oracle OSFamily = "Oracle"
|
||||
Orange OSFamily = "Orange"
|
||||
Osmosys OSFamily = "Osmosys"
|
||||
Ouya OSFamily = "Ouya"
|
||||
PCBSD OSFamily = "PC-BSD"
|
||||
PCMeasure OSFamily = "PCMeasure"
|
||||
PORTech OSFamily = "PORTech"
|
||||
Packard OSFamily = "Packard"
|
||||
Packet8 OSFamily = "Packet8"
|
||||
PacketFront OSFamily = "PacketFront"
|
||||
Packeteer OSFamily = "Packeteer"
|
||||
Palmmicro OSFamily = "Palmmicro"
|
||||
Palo OSFamily = "Palo"
|
||||
Panasas OSFamily = "Panasas"
|
||||
Panasonic OSFamily = "Panasonic"
|
||||
Papouch OSFamily = "Papouch"
|
||||
Patton OSFamily = "Patton"
|
||||
Peplink OSFamily = "Peplink"
|
||||
Perfectone OSFamily = "Perfectone"
|
||||
Perle OSFamily = "Perle"
|
||||
Phar OSFamily = "Phar"
|
||||
PheeNet OSFamily = "PheeNet"
|
||||
Philips OSFamily = "Philips"
|
||||
Phoenix OSFamily = "Phoenix"
|
||||
Pingtel OSFamily = "Pingtel"
|
||||
Pioneer OSFamily = "Pioneer"
|
||||
Pirelli OSFamily = "Pirelli"
|
||||
Planet OSFamily = "Planet"
|
||||
Polycom OSFamily = "Polycom"
|
||||
Precise OSFamily = "Precise"
|
||||
Printronix OSFamily = "Printronix"
|
||||
Priva OSFamily = "Priva"
|
||||
Promise OSFamily = "Promise"
|
||||
Proxim OSFamily = "Proxim"
|
||||
QEMU OSFamily = "QEMU"
|
||||
QNAP OSFamily = "QNAP"
|
||||
QNX OSFamily = "QNX"
|
||||
QTech OSFamily = "QTech"
|
||||
Qualisys OSFamily = "Qualisys"
|
||||
Quantum OSFamily = "Quantum"
|
||||
Quarterdeck OSFamily = "Quarterdeck"
|
||||
RAD OSFamily = "RAD"
|
||||
RCA OSFamily = "RCA"
|
||||
RF OSFamily = "RF"
|
||||
RFSpace OSFamily = "RF-Space"
|
||||
RGB OSFamily = "RGB"
|
||||
RIM OSFamily = "RIM"
|
||||
RISCOS OSFamily = "RISCOS"
|
||||
RISE OSFamily = "RISE"
|
||||
RSA OSFamily = "RSA"
|
||||
Rabbit OSFamily = "Rabbit"
|
||||
Radware OSFamily = "Radware"
|
||||
Raritan OSFamily = "Raritan"
|
||||
ReactOS OSFamily = "ReactOS"
|
||||
RedM OSFamily = "Red-M"
|
||||
Redback OSFamily = "Redback"
|
||||
Reliable OSFamily = "Reliable"
|
||||
Repotech OSFamily = "Repotech"
|
||||
Revo OSFamily = "Revo"
|
||||
Ricoh OSFamily = "Ricoh"
|
||||
Rigol OSFamily = "Rigol"
|
||||
Rio OSFamily = "Rio"
|
||||
Riverbed OSFamily = "Riverbed"
|
||||
Roberts OSFamily = "Roberts"
|
||||
Rockwell OSFamily = "Rockwell"
|
||||
Roku OSFamily = "Roku"
|
||||
Ruckus OSFamily = "Ruckus"
|
||||
RuggedCom OSFamily = "RuggedCom"
|
||||
Ruijie OSFamily = "Ruijie"
|
||||
SCO OSFamily = "SCO"
|
||||
SEH OSFamily = "SEH"
|
||||
SGI OSFamily = "SGI"
|
||||
SMA OSFamily = "SMA"
|
||||
SMC OSFamily = "SMC"
|
||||
SNR OSFamily = "SNR"
|
||||
Sagem OSFamily = "Sagem"
|
||||
Sagemcom OSFamily = "Sagemcom"
|
||||
Samsung OSFamily = "Samsung"
|
||||
Sandstrom OSFamily = "Sandstrom"
|
||||
Sanyo OSFamily = "Sanyo"
|
||||
Sapling OSFamily = "Sapling"
|
||||
Satel OSFamily = "Satel"
|
||||
Savin OSFamily = "Savin"
|
||||
Schneider OSFamily = "Schneider"
|
||||
Schrack OSFamily = "Schrack"
|
||||
Schweitzer OSFamily = "Schweitzer"
|
||||
Scientific OSFamily = "Scientific"
|
||||
Seagate OSFamily = "Seagate"
|
||||
Secure OSFamily = "Secure"
|
||||
Seiko OSFamily = "Seiko"
|
||||
Senao OSFamily = "Senao"
|
||||
Sensatronics OSFamily = "Sensatronics"
|
||||
Sequent OSFamily = "Sequent"
|
||||
Sharp OSFamily = "Sharp"
|
||||
Shenzhen OSFamily = "Shenzhen"
|
||||
ShoreTel OSFamily = "ShoreTel"
|
||||
Siemens OSFamily = "Siemens"
|
||||
Silicondust OSFamily = "Silicondust"
|
||||
Sinus OSFamily = "Sinus"
|
||||
Sipura OSFamily = "Sipura"
|
||||
Sitecom OSFamily = "Sitecom"
|
||||
Sling OSFamily = "Sling"
|
||||
Slingbox OSFamily = "Slingbox"
|
||||
Smart OSFamily = "Smart"
|
||||
Smartlink OSFamily = "Smartlink"
|
||||
Snom OSFamily = "Snom"
|
||||
Solwise OSFamily = "Solwise"
|
||||
SonicWALL OSFamily = "SonicWALL"
|
||||
Sonos OSFamily = "Sonos"
|
||||
Sonus OSFamily = "Sonus"
|
||||
Sony OSFamily = "Sony"
|
||||
Source OSFamily = "Source"
|
||||
Specialix OSFamily = "Specialix"
|
||||
Sphairon OSFamily = "Sphairon"
|
||||
Star OSFamily = "Star"
|
||||
Starbridge OSFamily = "Starbridge"
|
||||
Stonewater OSFamily = "Stonewater"
|
||||
StorageTek OSFamily = "StorageTek"
|
||||
Stratus OSFamily = "Stratus"
|
||||
Suga OSFamily = "Suga"
|
||||
Sun OSFamily = "Sun"
|
||||
SunPower OSFamily = "SunPower"
|
||||
Supermicro OSFamily = "Supermicro"
|
||||
Syllable OSFamily = "Syllable"
|
||||
Symantec OSFamily = "Symantec"
|
||||
Symbian OSFamily = "Symbian"
|
||||
Symbol OSFamily = "Symbol"
|
||||
Symmetricon OSFamily = "Symmetricon"
|
||||
Synology OSFamily = "Synology"
|
||||
THome OSFamily = "T-Home"
|
||||
TMarc OSFamily = "T-Marc"
|
||||
TPLINK OSFamily = "TP-LINK"
|
||||
TPLink OSFamily = "TP-Link"
|
||||
TRENDnet OSFamily = "TRENDnet"
|
||||
Tadiran OSFamily = "Tadiran"
|
||||
Tahoe OSFamily = "Tahoe"
|
||||
Tandberg OSFamily = "Tandberg"
|
||||
Tandem OSFamily = "Tandem"
|
||||
TechniSat OSFamily = "TechniSat"
|
||||
Tektronix OSFamily = "Tektronix"
|
||||
Telco OSFamily = "Telco"
|
||||
Teldat OSFamily = "Teldat"
|
||||
Telekom OSFamily = "Telekom"
|
||||
Telewell OSFamily = "Telewell"
|
||||
Telex OSFamily = "Telex"
|
||||
Telsey OSFamily = "Telsey"
|
||||
Teltronics OSFamily = "Teltronics"
|
||||
TenAsys OSFamily = "TenAsys"
|
||||
Tenda OSFamily = "Tenda"
|
||||
Teradici OSFamily = "Teradici"
|
||||
Terratec OSFamily = "Terratec"
|
||||
Texas OSFamily = "Texas"
|
||||
Thales OSFamily = "Thales"
|
||||
Thecus OSFamily = "Thecus"
|
||||
Thomson OSFamily = "Thomson"
|
||||
Tiandy OSFamily = "Tiandy"
|
||||
Tibbo OSFamily = "Tibbo"
|
||||
Tigo OSFamily = "Tigo"
|
||||
Tintri OSFamily = "Tintri"
|
||||
TippingPoint OSFamily = "TippingPoint"
|
||||
Tizen OSFamily = "Tizen"
|
||||
Topfield OSFamily = "Topfield"
|
||||
Toptech OSFamily = "Toptech"
|
||||
Toshiba OSFamily = "Toshiba"
|
||||
Trane OSFamily = "Trane"
|
||||
TransAct OSFamily = "TransAct"
|
||||
Tranzeo OSFamily = "Tranzeo"
|
||||
Trapeze OSFamily = "Trapeze"
|
||||
Tripp OSFamily = "Tripp"
|
||||
Tut OSFamily = "Tut"
|
||||
Tyco OSFamily = "Tyco"
|
||||
USRobotics OSFamily = "USRobotics"
|
||||
UTStarcom OSFamily = "UTStarcom"
|
||||
Ubee OSFamily = "Ubee"
|
||||
Ubicom OSFamily = "Ubicom"
|
||||
Ubiquiti OSFamily = "Ubiquiti"
|
||||
Universal OSFamily = "Universal"
|
||||
VBrick OSFamily = "VBrick"
|
||||
VIPA OSFamily = "VIPA"
|
||||
VMware OSFamily = "VMware"
|
||||
VTrak OSFamily = "VTrak"
|
||||
Vantage OSFamily = "Vantage"
|
||||
Vegastream OSFamily = "Vegastream"
|
||||
Viasat OSFamily = "Viasat"
|
||||
Vilar OSFamily = "Vilar"
|
||||
Virdi OSFamily = "Virdi"
|
||||
Visual OSFamily = "Visual"
|
||||
Vocality OSFamily = "Vocality"
|
||||
Vodafone OSFamily = "Vodafone"
|
||||
Vodavi OSFamily = "Vodavi"
|
||||
Vonage OSFamily = "Vonage"
|
||||
WandT OSFamily = "W&T"
|
||||
WAGO OSFamily = "WAGO"
|
||||
WIZnet OSFamily = "WIZnet"
|
||||
Wago OSFamily = "Wago"
|
||||
Walker OSFamily = "Walker"
|
||||
WatchGuard OSFamily = "WatchGuard"
|
||||
WebSense OSFamily = "WebSense"
|
||||
Welltech OSFamily = "Welltech"
|
||||
Westell OSFamily = "Westell"
|
||||
Westermo OSFamily = "Westermo"
|
||||
Western OSFamily = "Western"
|
||||
Wind OSFamily = "Wind"
|
||||
Windows OSFamily = "Windows"
|
||||
World OSFamily = "World"
|
||||
WowWee OSFamily = "WowWee"
|
||||
Wyse OSFamily = "Wyse"
|
||||
XAVi OSFamily = "XAVi"
|
||||
XEUdotCom OSFamily = "XEU.com"
|
||||
XMOS OSFamily = "XMOS"
|
||||
Xerox OSFamily = "Xerox"
|
||||
Xiaomi OSFamily = "Xiaomi"
|
||||
Xirrus OSFamily = "Xirrus"
|
||||
Xylan OSFamily = "Xylan"
|
||||
Xyplex OSFamily = "Xyplex"
|
||||
Yamaha OSFamily = "Yamaha"
|
||||
Yealink OSFamily = "Yealink"
|
||||
ZKTeco OSFamily = "ZKTeco"
|
||||
ZTE OSFamily = "ZTE"
|
||||
Zebra OSFamily = "Zebra"
|
||||
Zelax OSFamily = "Zelax"
|
||||
Zerto OSFamily = "Zerto"
|
||||
Zhone OSFamily = "Zhone"
|
||||
Zipato OSFamily = "Zipato"
|
||||
ZoneAlarm OSFamily = "ZoneAlarm"
|
||||
Zoom OSFamily = "Zoom"
|
||||
ZyXEL OSFamily = "ZyXEL"
|
||||
Zyfer OSFamily = "Zyfer"
|
||||
cab OSFamily = "cab"
|
||||
eCosCentric OSFamily = "eCosCentric"
|
||||
iDirect OSFamily = "iDirect"
|
||||
iPXE OSFamily = "iPXE"
|
||||
iRobot OSFamily = "iRobot"
|
||||
illumos OSFamily = "illumos"
|
||||
ipTIME OSFamily = "ipTIME"
|
||||
lwIP OSFamily = "lwIP"
|
||||
m3 OSFamily = "m3"
|
||||
mbNet OSFamily = "mbNet"
|
||||
nCircle OSFamily = "nCircle"
|
||||
)
|
||||
-508
@@ -1,508 +0,0 @@
|
||||
package nmap
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
family "github.com/Ullaakut/nmap/pkg/osfamilies"
|
||||
)
|
||||
|
||||
// Run represents an nmap scanning run.
|
||||
type Run struct {
|
||||
XMLName xml.Name `xml:"nmaprun"`
|
||||
|
||||
Args string `xml:"args,attr" json:"args"`
|
||||
ProfileName string `xml:"profile_name,attr" json:"profile_name"`
|
||||
Scanner string `xml:"scanner,attr" json:"scanner"`
|
||||
StartStr string `xml:"startstr,attr" json:"start_str"`
|
||||
Version string `xml:"version,attr" json:"version"`
|
||||
XMLOutputVersion string `xml:"xmloutputversion,attr" json:"xml_output_version"`
|
||||
Debugging Debugging `xml:"debugging" json:"debugging"`
|
||||
Stats Stats `xml:"runstats" json:"run_stats"`
|
||||
ScanInfo ScanInfo `xml:"scaninfo" json:"scan_info"`
|
||||
Start Timestamp `xml:"start,attr" json:"start"`
|
||||
Verbose Verbose `xml:"verbose" json:"verbose"`
|
||||
Hosts []Host `xml:"host" json:"hosts"`
|
||||
PostScripts []Script `xml:"postscript>script" json:"post_scripts"`
|
||||
PreScripts []Script `xml:"prescript>script" json:"pre_scripts"`
|
||||
Targets []Target `xml:"target" json:"targets"`
|
||||
TaskBegin []Task `xml:"taskbegin" json:"task_begin"`
|
||||
TaskProgress []TaskProgress `xml:"taskprogress" json:"task_progress"`
|
||||
TaskEnd []Task `xml:"taskend" json:"task_end"`
|
||||
|
||||
rawXML []byte
|
||||
}
|
||||
|
||||
// ToFile writes a Run as XML into the specified file path.
|
||||
func (r Run) ToFile(filePath string) error {
|
||||
return ioutil.WriteFile(filePath, r.rawXML, 0666)
|
||||
}
|
||||
|
||||
// ScanInfo represents the scan information.
|
||||
type ScanInfo struct {
|
||||
NumServices int `xml:"numservices,attr" json:"num_services"`
|
||||
Protocol string `xml:"protocol,attr" json:"protocol"`
|
||||
ScanFlags string `xml:"scanflags,attr" json:"scan_flags"`
|
||||
Services string `xml:"services,attr" json:"services"`
|
||||
Type string `xml:"type,attr" json:"type"`
|
||||
}
|
||||
|
||||
// Verbose contains the verbosity level of the scan.
|
||||
type Verbose struct {
|
||||
Level int `xml:"level,attr" json:"level"`
|
||||
}
|
||||
|
||||
// Debugging contains the debugging level of the scan.
|
||||
type Debugging struct {
|
||||
Level int `xml:"level,attr" json:"level"`
|
||||
}
|
||||
|
||||
// Task contains information about a task.
|
||||
type Task struct {
|
||||
Time Timestamp `xml:"time,attr" json:"time"`
|
||||
Task string `xml:"task,attr" json:"task"`
|
||||
ExtraInfo string `xml:"extrainfo,attr" json:"extra_info"`
|
||||
}
|
||||
|
||||
// TaskProgress contains information about the progression of a task.
|
||||
type TaskProgress struct {
|
||||
Percent float32 `xml:"percent,attr" json:"percent"`
|
||||
Remaining int `xml:"remaining,attr" json:"remaining"`
|
||||
Task string `xml:"task,attr" json:"task"`
|
||||
Etc Timestamp `xml:"etc,attr" json:"etc"`
|
||||
Time Timestamp `xml:"time,attr" json:"time"`
|
||||
}
|
||||
|
||||
// Target represents a target, how it was specified when passed to nmap,
|
||||
// its status and the reason for its status. Example:
|
||||
// <target specification="domain.does.not.exist" status="skipped" reason="invalid"/>
|
||||
type Target struct {
|
||||
Specification string `xml:"specification,attr" json:"specification"`
|
||||
Status string `xml:"status,attr" json:"status"`
|
||||
Reason string `xml:"reason,attr" json:"reason"`
|
||||
}
|
||||
|
||||
// Host represents a host that was scanned.
|
||||
type Host struct {
|
||||
Distance Distance `xml:"distance" json:"distance"`
|
||||
EndTime Timestamp `xml:"endtime,attr,omitempty" json:"end_time"`
|
||||
IPIDSequence IPIDSequence `xml:"ipidsequence" json:"ip_id_sequence"`
|
||||
OS OS `xml:"os" json:"os"`
|
||||
StartTime Timestamp `xml:"starttime,attr,omitempty" json:"start_time"`
|
||||
Status Status `xml:"status" json:"status"`
|
||||
TCPSequence TCPSequence `xml:"tcpsequence" json:"tcp_sequence"`
|
||||
TCPTSSequence TCPTSSequence `xml:"tcptssequence" json:"tcp_ts_sequence"`
|
||||
Times Times `xml:"times" json:"times"`
|
||||
Trace Trace `xml:"trace" json:"trace"`
|
||||
Uptime Uptime `xml:"uptime" json:"uptime"`
|
||||
Comment string `xml:"comment,attr" json:"comment"`
|
||||
Addresses []Address `xml:"address" json:"addresses"`
|
||||
ExtraPorts []ExtraPort `xml:"ports>extraports" json:"extra_ports"`
|
||||
Hostnames []Hostname `xml:"hostnames>hostname" json:"hostnames"`
|
||||
HostScripts []Script `xml:"hostscript>script" json:"host_scripts"`
|
||||
Ports []Port `xml:"ports>port" json:"ports"`
|
||||
Smurfs []Smurf `xml:"smurf" json:"smurfs"`
|
||||
}
|
||||
|
||||
// Status represents a host's status.
|
||||
type Status struct {
|
||||
State string `xml:"state,attr" json:"state"`
|
||||
Reason string `xml:"reason,attr" json:"reason"`
|
||||
ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
|
||||
}
|
||||
|
||||
func (s Status) String() string {
|
||||
return s.State
|
||||
}
|
||||
|
||||
// Address contains a IPv4 or IPv6 address for a host.
|
||||
type Address struct {
|
||||
Addr string `xml:"addr,attr" json:"addr"`
|
||||
AddrType string `xml:"addrtype,attr" json:"addr_type"`
|
||||
Vendor string `xml:"vendor,attr" json:"vendor"`
|
||||
}
|
||||
|
||||
func (a Address) String() string {
|
||||
return a.Addr
|
||||
}
|
||||
|
||||
// Hostname is a name for a host.
|
||||
type Hostname struct {
|
||||
Name string `xml:"name,attr" json:"name"`
|
||||
Type string `xml:"type,attr" json:"type"`
|
||||
}
|
||||
|
||||
func (h Hostname) String() string {
|
||||
return h.Name
|
||||
}
|
||||
|
||||
// Smurf contains repsonses from a smurf attack.
|
||||
type Smurf struct {
|
||||
Responses string `xml:"responses,attr" json:"responses"`
|
||||
}
|
||||
|
||||
// ExtraPort contains the information about the closed and filtered ports.
|
||||
type ExtraPort struct {
|
||||
State string `xml:"state,attr" json:"state"`
|
||||
Count int `xml:"count,attr" json:"count"`
|
||||
Reasons []Reason `xml:"extrareasons" json:"reasons"`
|
||||
}
|
||||
|
||||
// Reason represents a reason why a port is closed or filtered.
|
||||
// This won't be in the scan results unless WithReason is used.
|
||||
type Reason struct {
|
||||
Reason string `xml:"reason,attr" json:"reason"`
|
||||
Count int `xml:"count,attr" json:"count"`
|
||||
}
|
||||
|
||||
// Port contains all the information about a scanned port.
|
||||
type Port struct {
|
||||
ID uint16 `xml:"portid,attr" json:"id"`
|
||||
Protocol string `xml:"protocol,attr" json:"protocol"`
|
||||
Owner Owner `xml:"owner" json:"owner"`
|
||||
Service Service `xml:"service" json:"service"`
|
||||
State State `xml:"state" json:"state"`
|
||||
Scripts []Script `xml:"script" json:"scripts"`
|
||||
}
|
||||
|
||||
// PortStatus represents a port's state.
|
||||
type PortStatus string
|
||||
|
||||
// Enumerates the different possible state values.
|
||||
const (
|
||||
Open PortStatus = "open"
|
||||
Closed PortStatus = "closed"
|
||||
Filtered PortStatus = "filtered"
|
||||
Unfiltered PortStatus = "unfiltered"
|
||||
)
|
||||
|
||||
// Status returns the status of a port.
|
||||
func (p Port) Status() PortStatus {
|
||||
return PortStatus(p.State.State)
|
||||
}
|
||||
|
||||
// State contains information about a given port's status.
|
||||
// State will be open, closed, etc.
|
||||
type State struct {
|
||||
State string `xml:"state,attr" json:"state"`
|
||||
Reason string `xml:"reason,attr" json:"reason"`
|
||||
ReasonIP string `xml:"reason_ip,attr" json:"reason_ip"`
|
||||
ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
|
||||
}
|
||||
|
||||
func (s State) String() string {
|
||||
return s.State
|
||||
}
|
||||
|
||||
// Owner contains the name of a port's owner.
|
||||
type Owner struct {
|
||||
Name string `xml:"name,attr" json:"name"`
|
||||
}
|
||||
|
||||
func (o Owner) String() string {
|
||||
return o.Name
|
||||
}
|
||||
|
||||
// Service contains detailed information about a service on an open port.
|
||||
type Service struct {
|
||||
DeviceType string `xml:"devicetype,attr" json:"device_type"`
|
||||
ExtraInfo string `xml:"extrainfo,attr" json:"extra_info"`
|
||||
HighVersion string `xml:"highver,attr" json:"high_version"`
|
||||
Hostname string `xml:"hostname,attr" json:"hostname"`
|
||||
LowVersion string `xml:"lowver,attr" json:"low_version"`
|
||||
Method string `xml:"method,attr" json:"method"`
|
||||
Name string `xml:"name,attr" json:"name"`
|
||||
OSType string `xml:"ostype,attr" json:"os_type"`
|
||||
Product string `xml:"product,attr" json:"product"`
|
||||
Proto string `xml:"proto,attr" json:"proto"`
|
||||
RPCNum string `xml:"rpcnum,attr" json:"rpc_num"`
|
||||
ServiceFP string `xml:"servicefp,attr" json:"service_fp"`
|
||||
Tunnel string `xml:"tunnel,attr" json:"tunnel"`
|
||||
Version string `xml:"version,attr" json:"version"`
|
||||
Configuration int `xml:"conf,attr" json:"configuration"`
|
||||
CPEs []CPE `xml:"cpe" json:"cpes"`
|
||||
}
|
||||
|
||||
func (s Service) String() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
// CPE (Common Platform Enumeration) is a standardized way to name software
|
||||
// applications, operating systems and hardware platforms.
|
||||
type CPE string
|
||||
|
||||
// Script represents an Nmap Scripting Engine script.
|
||||
type Script struct {
|
||||
ID string `xml:"id,attr" json:"id"`
|
||||
Output string `xml:"output,attr" json:"output"`
|
||||
Tables Table `xml:"table" json:"tables"`
|
||||
}
|
||||
|
||||
// Table contains the output of the script in an easily parsable form.
|
||||
type Table map[string]string
|
||||
|
||||
// MarshalXML implements the xml.Marshaler interface.
|
||||
func (t Table) MarshalXML(e *xml.Encoder, startElem xml.StartElement) error {
|
||||
tokens := []xml.Token{startElem}
|
||||
|
||||
// Add all key/value pairs as entries in the XML array.
|
||||
for key, value := range t {
|
||||
// Start of the XML element.
|
||||
start := xml.StartElement{
|
||||
Name: xml.Name{
|
||||
Local: "elem",
|
||||
},
|
||||
Attr: []xml.Attr{
|
||||
{
|
||||
Name: xml.Name{
|
||||
Local: "key",
|
||||
},
|
||||
Value: key,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// End of the XML element.
|
||||
end := xml.EndElement{
|
||||
Name: start.Name,
|
||||
}
|
||||
|
||||
// Append the start, content and end of the new element to the list of XML tokens.
|
||||
tokens = append(tokens, start, xml.CharData(value), end)
|
||||
}
|
||||
|
||||
tokens = append(tokens, xml.EndElement{
|
||||
Name: startElem.Name,
|
||||
})
|
||||
|
||||
// Encode all tokens.
|
||||
for _, t := range tokens {
|
||||
err := e.EncodeToken(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the encoder to ensure that the tokens are written.
|
||||
err := e.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalXML implements the xml.Unmarshaler interface.
|
||||
func (t *Table) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
table := make(map[string]string)
|
||||
|
||||
var (
|
||||
currentKey string
|
||||
currentValue string
|
||||
)
|
||||
|
||||
for {
|
||||
token, err := d.Token()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
switch element := token.(type) {
|
||||
case xml.StartElement:
|
||||
for _, attribute := range element.Attr {
|
||||
if attribute.Name.Local == "key" {
|
||||
currentKey = attribute.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
case xml.CharData:
|
||||
currentValue = string(element)
|
||||
case xml.EndElement:
|
||||
// Insert the current key/value pair.
|
||||
table[currentKey] = currentValue
|
||||
|
||||
// Reset the temporary variables for the next pair.
|
||||
currentKey = ""
|
||||
currentValue = ""
|
||||
}
|
||||
}
|
||||
|
||||
*t = table
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OS contains the fingerprinted operating system for a host.
|
||||
type OS struct {
|
||||
PortsUsed []PortUsed `xml:"portused" json:"ports_used"`
|
||||
Matches []OSMatch `xml:"osmatch" json:"os_matches"`
|
||||
Fingerprints []OSFingerprint `xml:"osfingerprint" json:"os_fingerprints"`
|
||||
Classes []OSClass `xml:"osclass" json:"os_classes"`
|
||||
}
|
||||
|
||||
// PortUsed is the port used to fingerprint an operating system.
|
||||
type PortUsed struct {
|
||||
State string `xml:"state,attr" json:"state"`
|
||||
Proto string `xml:"proto,attr" json:"proto"`
|
||||
ID int `xml:"portid,attr" json:"port_id"`
|
||||
}
|
||||
|
||||
// OSMatch contains detailed information regarding an operating system fingerprint.
|
||||
type OSMatch struct {
|
||||
Name string `xml:"name,attr" json:"name"`
|
||||
Accuracy int `xml:"accuracy,attr" json:"accuracy"`
|
||||
Line int `xml:"line,attr" json:"line"`
|
||||
}
|
||||
|
||||
// OSClass contains vendor information about an operating system.
|
||||
type OSClass struct {
|
||||
Vendor string `xml:"vendor,attr" json:"vendor"`
|
||||
OSGeneration string `xml:"osgen,attr" json:"os_generation"`
|
||||
Type string `xml:"type,attr" json:"type"`
|
||||
Accuracy int `xml:"accuracy,attr" json:"accuracy"`
|
||||
Family string `xml:"osfamily,attr" json:"os_family"`
|
||||
CPEs []CPE `xml:"cpe" json:"cpes"`
|
||||
}
|
||||
|
||||
// OSFamily returns the OS family in an enumerated format.
|
||||
func (o OSClass) OSFamily() family.OSFamily {
|
||||
return family.OSFamily(o.Family)
|
||||
}
|
||||
|
||||
// OSFingerprint is the actual fingerprint string of an operating system.
|
||||
type OSFingerprint struct {
|
||||
Fingerprint string `xml:"fingerprint,attr" json:"fingerprint"`
|
||||
}
|
||||
|
||||
// Distance is the amount of hops to a particular host.
|
||||
type Distance struct {
|
||||
Value int `xml:"value,attr" json:"value"`
|
||||
}
|
||||
|
||||
// Uptime is the amount of time the host has been up.
|
||||
type Uptime struct {
|
||||
Seconds int `xml:"seconds,attr" json:"seconds"`
|
||||
Lastboot string `xml:"lastboot,attr" json:"last_boot"`
|
||||
}
|
||||
|
||||
// Sequence represents a detected sequence.
|
||||
type Sequence struct {
|
||||
Class string `xml:"class,attr" json:"class"`
|
||||
Values string `xml:"values,attr" json:"values"`
|
||||
}
|
||||
|
||||
// TCPSequence represents a detected TCP sequence.
|
||||
type TCPSequence struct {
|
||||
Index int `xml:"index,attr" json:"index"`
|
||||
Difficulty string `xml:"difficulty,attr" json:"difficulty"`
|
||||
Values string `xml:"values,attr" json:"values"`
|
||||
}
|
||||
|
||||
// IPIDSequence represents a detected IP ID sequence.
|
||||
type IPIDSequence Sequence
|
||||
|
||||
// TCPTSSequence represents a detected TCP TS sequence.
|
||||
type TCPTSSequence Sequence
|
||||
|
||||
// Trace represents the trace to a host, including the hops.
|
||||
type Trace struct {
|
||||
Proto string `xml:"proto,attr" json:"proto"`
|
||||
Port int `xml:"port,attr" json:"port"`
|
||||
Hops []Hop `xml:"hop" json:"hops"`
|
||||
}
|
||||
|
||||
// Hop is an IP hop to a host.
|
||||
type Hop struct {
|
||||
TTL float32 `xml:"ttl,attr" json:"ttl"`
|
||||
RTT string `xml:"rtt,attr" json:"rtt"`
|
||||
IPAddr string `xml:"ipaddr,attr" json:"ip_addr"`
|
||||
Host string `xml:"host,attr" json:"host"`
|
||||
}
|
||||
|
||||
// Times contains time statistics for an nmap scan.
|
||||
type Times struct {
|
||||
SRTT string `xml:"srtt,attr" json:"srtt"`
|
||||
RTT string `xml:"rttvar,attr" json:"rttv"`
|
||||
To string `xml:"to,attr" json:"to"`
|
||||
}
|
||||
|
||||
// Stats contains statistics for an nmap scan.
|
||||
type Stats struct {
|
||||
Finished Finished `xml:"finished" json:"finished"`
|
||||
Hosts HostStats `xml:"hosts" json:"hosts"`
|
||||
}
|
||||
|
||||
// Finished contains detailed statistics regarding a finished scan.
|
||||
type Finished struct {
|
||||
Time Timestamp `xml:"time,attr" json:"time"`
|
||||
TimeStr string `xml:"timestr,attr" json:"time_str"`
|
||||
Elapsed float32 `xml:"elapsed,attr" json:"elapsed"`
|
||||
Summary string `xml:"summary,attr" json:"summary"`
|
||||
Exit string `xml:"exit,attr" json:"exit"`
|
||||
ErrorMsg string `xml:"errormsg,attr" json:"error_msg"`
|
||||
}
|
||||
|
||||
// HostStats contains the amount of up and down hosts and the total count.
|
||||
type HostStats struct {
|
||||
Up int `xml:"up,attr" json:"up"`
|
||||
Down int `xml:"down,attr" json:"down"`
|
||||
Total int `xml:"total,attr" json:"total"`
|
||||
}
|
||||
|
||||
// Timestamp represents time as a UNIX timestamp in seconds.
|
||||
type Timestamp time.Time
|
||||
|
||||
// ParseTime converts a UNIX timestamp string to a time.Time.
|
||||
func (t *Timestamp) ParseTime(s string) error {
|
||||
timestamp, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = Timestamp(time.Unix(timestamp, 0))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatTime formats the time.Time value as a UNIX timestamp string.
|
||||
func (t Timestamp) FormatTime() string {
|
||||
return strconv.FormatInt(time.Time(t).Unix(), 10)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (t Timestamp) MarshalJSON() ([]byte, error) {
|
||||
return []byte(t.FormatTime()), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (t *Timestamp) UnmarshalJSON(b []byte) error {
|
||||
return t.ParseTime(string(b))
|
||||
}
|
||||
|
||||
// MarshalXMLAttr implements the xml.MarshalerAttr interface.
|
||||
func (t Timestamp) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
|
||||
if time.Time(t).IsZero() {
|
||||
return xml.Attr{}, nil
|
||||
}
|
||||
|
||||
return xml.Attr{Name: name, Value: t.FormatTime()}, nil
|
||||
}
|
||||
|
||||
// UnmarshalXMLAttr implements the xml.UnmarshalXMLAttr interface.
|
||||
func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
|
||||
return t.ParseTime(attr.Value)
|
||||
}
|
||||
|
||||
// Parse takes a byte array of nmap xml data and unmarshals it into a
|
||||
// Run struct.
|
||||
func Parse(content []byte) (*Run, error) {
|
||||
r := &Run{
|
||||
rawXML: content,
|
||||
}
|
||||
|
||||
err := xml.Unmarshal(content, r)
|
||||
|
||||
return r, err
|
||||
}
|
||||
-939
@@ -1,939 +0,0 @@
|
||||
package nmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
family "github.com/Ullaakut/nmap/pkg/osfamilies"
|
||||
)
|
||||
|
||||
func TestParseTime(t *testing.T) {
|
||||
ts := Timestamp{}
|
||||
|
||||
err := ts.ParseTime("invalid")
|
||||
if err == nil {
|
||||
t.Errorf("expected strconv.ParseInt: parsing \"invalid\": invalid syntax got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatTime(t *testing.T) {
|
||||
originalStr := "123456789"
|
||||
ts := Timestamp{}
|
||||
|
||||
err := ts.ParseTime(originalStr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
result := ts.FormatTime()
|
||||
|
||||
if result != originalStr {
|
||||
t.Errorf("expected %s got %s", originalStr, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOSFamily(t *testing.T) {
|
||||
osc := OSClass{
|
||||
Family: "Linux",
|
||||
}
|
||||
|
||||
if osc.OSFamily() != family.Linux {
|
||||
t.Errorf("expected OSClass.OSFamily() to be equal to %v, got %v", family.Linux, osc.OSFamily())
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTableXML(t *testing.T) {
|
||||
expectedTable := map[string]string{
|
||||
"key": "AAAAB3NzaC1yc2EAAAABIwAAAQEAwVKoTY/7GFG7BmKkG6qFAHY/f3ciDX2MXTBLMEJP0xyUJsoy/CVRYw2b4qUB/GCJ5lh2InP+LVnPD3ZdtpyIvbS0eRZs/BH+mVLGh9xA/wOEUiiCfzQRsHj1xn7cqeWViAzQtdGluk/5CVAvr1FU3HNaaWkg7KQOSiKAzgDwCBtQhlgI40xdXgbqMkrHeP4M1p4MxoEVpZMe4oObACWwazeHP/Xas1vy5rbnmE59MpEZaA8t7AfGlW4MrVMhAB1JsFMdd0qFLpy/l93H3ptSlx1+6PQ5gUyjhmDUjMR+k6fb0yOeGdOrjN8IrWPmebZRFBjK5aCJwubgY/03VsSBMQ==",
|
||||
"fingerprint": "79f809acd4e232421049d3bd208285ec",
|
||||
"type": "ssh-rsa",
|
||||
"bits": "2048",
|
||||
}
|
||||
|
||||
input := []byte(fmt.Sprintf(
|
||||
`<table><elem key="key">%s</elem><elem key="fingerprint">%s</elem><elem key="type">%s</elem><elem key="bits">%s</elem></table>`,
|
||||
expectedTable["key"],
|
||||
expectedTable["fingerprint"],
|
||||
expectedTable["type"],
|
||||
expectedTable["bits"],
|
||||
))
|
||||
|
||||
var table Table
|
||||
|
||||
err := xml.Unmarshal(input, &table)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if table["key"] != expectedTable["key"] {
|
||||
t.Errorf("expected %v got %v", expectedTable["key"], table["key"])
|
||||
}
|
||||
|
||||
if table["fingerprint"] != expectedTable["fingerprint"] {
|
||||
t.Errorf("expected %v got %v", expectedTable["fingerprint"], table["fingerprint"])
|
||||
}
|
||||
|
||||
if table["type"] != expectedTable["type"] {
|
||||
t.Errorf("expected %v got %v", expectedTable["type"], table["type"])
|
||||
}
|
||||
|
||||
if table["bits"] != expectedTable["bits"] {
|
||||
t.Errorf("expected %v got %v", expectedTable["bits"], table["bits"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatTableXML(t *testing.T) {
|
||||
table := Table(map[string]string{
|
||||
"key": "AAAAB3NzaC1yc2EAAAABIwAAAQEAwVKoTY/7GFG7BmKkG6qFAHY/f3ciDX2MXTBLMEJP0xyUJsoy/CVRYw2b4qUB/GCJ5lh2InP+LVnPD3ZdtpyIvbS0eRZs/BH+mVLGh9xA/wOEUiiCfzQRsHj1xn7cqeWViAzQtdGluk/5CVAvr1FU3HNaaWkg7KQOSiKAzgDwCBtQhlgI40xdXgbqMkrHeP4M1p4MxoEVpZMe4oObACWwazeHP/Xas1vy5rbnmE59MpEZaA8t7AfGlW4MrVMhAB1JsFMdd0qFLpy/l93H3ptSlx1+6PQ5gUyjhmDUjMR+k6fb0yOeGdOrjN8IrWPmebZRFBjK5aCJwubgY/03VsSBMQ==",
|
||||
"fingerprint": "79f809acd4e232421049d3bd208285ec",
|
||||
"type": "ssh-rsa",
|
||||
"bits": "2048",
|
||||
})
|
||||
|
||||
expectedXML := [][]byte{
|
||||
[]byte("<Table>"),
|
||||
[]byte(fmt.Sprintf(`<elem key="key">%s</elem>`, table["key"])),
|
||||
[]byte(fmt.Sprintf(`<elem key="fingerprint">%s</elem>`, table["fingerprint"])),
|
||||
[]byte(fmt.Sprintf(`<elem key="type">%s</elem>`, table["type"])),
|
||||
[]byte(fmt.Sprintf(`<elem key="bits">%s</elem>`, table["bits"])),
|
||||
[]byte("</Table>"),
|
||||
}
|
||||
|
||||
XML, err := xml.Marshal(table)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, expectedXMLElement := range expectedXML {
|
||||
if !bytes.Contains(XML, expectedXMLElement) {
|
||||
t.Errorf("missing %s in %s", expectedXMLElement, XML)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRunXML(t *testing.T) {
|
||||
tests := []struct {
|
||||
inputFile string
|
||||
|
||||
expectedResult *Run
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
inputFile: "test_xml/scan01.xml",
|
||||
|
||||
expectedResult: &Run{
|
||||
Args: "nmap -A -v -oX sample-03.xml freshmeat.net sourceforge.net nmap.org kernel.org openbsd.org netbsd.org google.com gmail.com",
|
||||
Scanner: "nmap",
|
||||
StartStr: "Sun Jan 27 21:10:02 2008",
|
||||
Version: "4.53",
|
||||
XMLOutputVersion: "1.01",
|
||||
ScanInfo: ScanInfo{
|
||||
NumServices: 1714,
|
||||
Protocol: "tcp",
|
||||
Services: "1-1027,1029-1033,1040,1043,1050,1058-1059,1067-1068,1076,1080,1083-1084,1103,1109-1110,1112,1127,1139,1155,1158,1178,1212,1214,1220,1222,1234,1241,1248,1270,1337,1346-1381,1383-1552,1600,1650-1652,1661-1672,1680,1720,1723,1755,1761-1764,1827,1900,1935,1984,1986-2028,2030,2032-2035,2038,2040-2049,2053,2064-2065,2067-2068,2105-2106,2108,2111-2112,2120-2121,2201,2232,2241,2301,2307,2401,2430-2433,2500-2501,2564,2600-2605,2627-2628,2638,2766,2784,2809,2903,2998,3000-3001,3005-3006,3025,3045,3049,3052,3064,3086,3128,3141,3264,3268-3269,3292,3299,3306,3333,3372,3389,3397-3399,3421,3455-3457,3462,3531,3632,3689,3900,3984-3986,3999-4000,4002,4008,4045,4125,4132-4133,4144,4199,4224,4321,4333,4343,4444,4480,4500,4557,4559,4660,4662,4672,4899,4987,4998,5000-5003,5009-5011,5050,5060,5100-5102,5145,5190-5193,5232,5236,5300-5305,5308,5400,5405,5432,5490,5500,5510,5520,5530,5540,5550,5555,5560,5631-5632,5679-5680,5713-5717,5800-5803,5900-5903,5977-5979,5997-6009,6017,6050,6101,6103,6105-6106,6110-6112,6141-6148,6222,6346-6347,6400-6401,6502,6543-6544,6547-6548,6558,6588,6662,6665-6670,6699-6701,6881,6969,7000-7010,7070,7100,7200-7201,7273,7326,7464,7597,7937-7938,8000,8007,8009,8021,8076,8080-8082,8118,8123,8443,8770,8888,8892,9040,9050-9051,9090,9100-9107,9111,9152,9535,9876,9991-9992,9999-10000,10005,10082-10083,11371,12000,12345-12346,13701-13702,13705-13706,13708-13718,13720-13722,13782-13783,14141,15126,15151,16080,16444,16959,17007,17300,18000,18181-18185,18187,19150,20005,22273,22289,22305,22321,22370,26208,27000-27010,27374,27665,31337,31416,32770-32780,32786-32787,38037,38292,43188,44334,44442-44443,47557,49400,50000,50002,54320,61439-61441,65301",
|
||||
Type: "syn",
|
||||
},
|
||||
Start: Timestamp(time.Unix(1201479002, 0)),
|
||||
Verbose: Verbose{
|
||||
Level: 1,
|
||||
},
|
||||
Stats: Stats{
|
||||
Finished: Finished{
|
||||
Time: Timestamp(time.Unix(1201481569, 0)),
|
||||
TimeStr: "Sun Jan 27 21:52:49 2008",
|
||||
},
|
||||
Hosts: HostStats{
|
||||
Up: 8,
|
||||
Total: 8,
|
||||
Down: 0,
|
||||
},
|
||||
},
|
||||
Hosts: []Host{
|
||||
{
|
||||
IPIDSequence: IPIDSequence{
|
||||
Class: "All zeros",
|
||||
Values: "0,0,0,0,0,0",
|
||||
},
|
||||
OS: OS{
|
||||
PortsUsed: []PortUsed{
|
||||
{
|
||||
State: "open",
|
||||
Proto: "tcp",
|
||||
ID: 80,
|
||||
},
|
||||
{
|
||||
State: "closed",
|
||||
Proto: "tcp",
|
||||
ID: 443,
|
||||
},
|
||||
},
|
||||
Matches: []OSMatch{
|
||||
{
|
||||
Name: "MicroTik RouterOS 2.9.46",
|
||||
Accuracy: 94,
|
||||
Line: 14788,
|
||||
},
|
||||
{
|
||||
Name: "Linksys WRT54GS WAP (Linux kernel)",
|
||||
Accuracy: 94,
|
||||
Line: 8292,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.4.18 - 2.4.32 (likely embedded)",
|
||||
Accuracy: 94,
|
||||
Line: 8499,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.4.21 - 2.4.33",
|
||||
Accuracy: 94,
|
||||
Line: 8624,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.4.27",
|
||||
Accuracy: 94,
|
||||
Line: 8675,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.4.28 - 2.4.30",
|
||||
Accuracy: 94,
|
||||
Line: 8693,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.6.5 - 2.6.18",
|
||||
Accuracy: 94,
|
||||
Line: 11411,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.6.8",
|
||||
Accuracy: 94,
|
||||
Line: 11485,
|
||||
},
|
||||
{
|
||||
Name: "WebVOIZE 120 IP phone",
|
||||
Accuracy: 94,
|
||||
Line: 18921,
|
||||
},
|
||||
{
|
||||
Name: "Linux 2.4.2 (Red Hat 7.1)",
|
||||
Accuracy: 91,
|
||||
Line: 8533,
|
||||
},
|
||||
},
|
||||
Fingerprints: []OSFingerprint{
|
||||
{
|
||||
Fingerprint: fingerprint,
|
||||
},
|
||||
},
|
||||
Classes: []OSClass{
|
||||
{
|
||||
Vendor: "MikroTik",
|
||||
OSGeneration: "2.X",
|
||||
Type: "software router",
|
||||
Accuracy: 94,
|
||||
Family: "RouterOS",
|
||||
},
|
||||
{
|
||||
Vendor: "Linksys",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 94,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "general purpose",
|
||||
Accuracy: 94,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.6.X",
|
||||
Type: "general purpose",
|
||||
Accuracy: 94,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "WebVOIZE",
|
||||
Type: "VoIP phone",
|
||||
Accuracy: 94,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "D-Link",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Inventel",
|
||||
Type: "WAP",
|
||||
Accuracy: 91,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "USRobotics",
|
||||
Type: "broadband router",
|
||||
Accuracy: 91,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "broadband router",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "media device",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Linux",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "VoIP gateway",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Netgear",
|
||||
Type: "WAP",
|
||||
Accuracy: 91,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "QLogic",
|
||||
Type: "switch",
|
||||
Accuracy: 91,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Sharp",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "PDA",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "FON",
|
||||
OSGeneration: "2.6.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 91,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "FON",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 90,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Belkin",
|
||||
Type: "WAP",
|
||||
Accuracy: 90,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Asus",
|
||||
Type: "WAP",
|
||||
Accuracy: 90,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Netgear",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "WAP",
|
||||
Accuracy: 90,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Xerox",
|
||||
Type: "printer",
|
||||
Accuracy: 90,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Aladdin",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "security-misc",
|
||||
Accuracy: 89,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Occam",
|
||||
Type: "VoIP gateway",
|
||||
Accuracy: 89,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Roku",
|
||||
Type: "media device",
|
||||
Accuracy: 89,
|
||||
Family: "embedded",
|
||||
},
|
||||
{
|
||||
Vendor: "Siemens",
|
||||
Type: "WAP",
|
||||
Accuracy: 89,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "3Com",
|
||||
OSGeneration: "2.4.X",
|
||||
Type: "broadband router",
|
||||
Accuracy: 89,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Dream Multimedia",
|
||||
OSGeneration: "2.6.X",
|
||||
Type: "media device",
|
||||
Accuracy: 89,
|
||||
Family: "Linux",
|
||||
},
|
||||
{
|
||||
Vendor: "Iomega",
|
||||
OSGeneration: "2.6.X",
|
||||
Type: "storage-misc",
|
||||
Accuracy: 89,
|
||||
Family: "Linux",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: Status{
|
||||
State: "up",
|
||||
Reason: "reset",
|
||||
},
|
||||
TCPSequence: TCPSequence{
|
||||
Index: 242,
|
||||
Difficulty: "Good luck!",
|
||||
Values: "457B276,4584FC8,161C122C,161B185F,1605EA95,1614C498",
|
||||
},
|
||||
TCPTSSequence: TCPTSSequence{
|
||||
Class: "other",
|
||||
Values: "3FB03AA9,3FB03C75,45B26360,45B2636A,45B26374,45B2637E",
|
||||
},
|
||||
Times: Times{
|
||||
SRTT: "269788",
|
||||
RTT: "41141",
|
||||
To: "434352",
|
||||
},
|
||||
Trace: Trace{
|
||||
Proto: "tcp",
|
||||
Port: 80,
|
||||
Hops: []Hop{
|
||||
{
|
||||
TTL: 1,
|
||||
RTT: "1.83",
|
||||
IPAddr: "192.168.254.254",
|
||||
},
|
||||
{
|
||||
TTL: 2,
|
||||
RTT: "18.95",
|
||||
IPAddr: "200.217.89.32",
|
||||
},
|
||||
{
|
||||
TTL: 3,
|
||||
RTT: "18.33",
|
||||
IPAddr: "200.217.30.250",
|
||||
Host: "gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br",
|
||||
},
|
||||
{
|
||||
TTL: 4,
|
||||
RTT: "45.05",
|
||||
IPAddr: "200.97.65.250",
|
||||
Host: "pos15-1-nbv-pe-rotd-03.telemar.net.br",
|
||||
},
|
||||
{
|
||||
TTL: 5,
|
||||
RTT: "43.49",
|
||||
IPAddr: "200.223.131.13",
|
||||
Host: "pos6-0-nbv-pe-rotn-01.telemar.net.br",
|
||||
},
|
||||
{
|
||||
TTL: 6,
|
||||
RTT: "91.27",
|
||||
IPAddr: "200.223.131.205",
|
||||
Host: "so-0-2-0-0-arc-rj-rotn-01.telemar.net.br",
|
||||
},
|
||||
{
|
||||
TTL: 8,
|
||||
RTT: "191.87",
|
||||
IPAddr: "200.223.131.110",
|
||||
Host: "PO0-3.ARC-RJ-ROTN-01.telemar.net.br",
|
||||
},
|
||||
{
|
||||
TTL: 9,
|
||||
RTT: "177.30",
|
||||
IPAddr: "208.173.90.89",
|
||||
Host: "bpr2-so-5-2-0.miamimit.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 10,
|
||||
RTT: "181.50",
|
||||
IPAddr: "208.172.97.169",
|
||||
Host: "cr2-pos-0-3-1-0.miami.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 11,
|
||||
RTT: "336.43",
|
||||
IPAddr: "206.24.210.70",
|
||||
Host: "cr1-loopback.sfo.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 12,
|
||||
RTT: "245.32",
|
||||
IPAddr: "204.70.200.229",
|
||||
Host: "er1-te-1-0-1.SanJose3Equinix.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 13,
|
||||
RTT: "238.47",
|
||||
IPAddr: "204.70.200.210",
|
||||
Host: "hr1-te-2-0-0.santaclarasc4.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 14,
|
||||
RTT: "322.90",
|
||||
IPAddr: "204.70.200.217",
|
||||
Host: "hr1-te-2-0-0.santaclarasc9.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 15,
|
||||
RTT: "330.96",
|
||||
IPAddr: "204.70.203.146",
|
||||
},
|
||||
{
|
||||
TTL: 16,
|
||||
RTT: "342.57",
|
||||
IPAddr: "66.35.194.59",
|
||||
Host: "csr2-ve242.santaclarasc8.savvis.net",
|
||||
},
|
||||
{
|
||||
TTL: 17,
|
||||
RTT: "248.22",
|
||||
IPAddr: "66.35.210.202",
|
||||
},
|
||||
{
|
||||
TTL: 18,
|
||||
RTT: "238.36",
|
||||
IPAddr: "66.35.250.168",
|
||||
Host: "freshmeat.net",
|
||||
},
|
||||
},
|
||||
},
|
||||
Uptime: Uptime{
|
||||
Seconds: 206,
|
||||
Lastboot: "Sun Jan 27 21:43:11 2008",
|
||||
},
|
||||
Addresses: []Address{
|
||||
{
|
||||
Addr: "66.35.250.168",
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
},
|
||||
ExtraPorts: []ExtraPort{
|
||||
{
|
||||
State: "filtered",
|
||||
Count: 1712,
|
||||
Reasons: []Reason{
|
||||
{
|
||||
Reason: "host-prohibiteds",
|
||||
Count: 1712,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Hostnames: []Hostname{
|
||||
{
|
||||
Name: "freshmeat.net",
|
||||
Type: "PTR",
|
||||
},
|
||||
},
|
||||
Ports: []Port{
|
||||
{
|
||||
ID: 80,
|
||||
Protocol: "tcp",
|
||||
Service: Service{
|
||||
Name: "http",
|
||||
ExtraInfo: "(Unix) PHP/4.4.7",
|
||||
Method: "probed",
|
||||
Product: "Apache httpd",
|
||||
Version: "1.3.39",
|
||||
Configuration: 10,
|
||||
},
|
||||
State: State{
|
||||
State: "open",
|
||||
Reason: "syn-ack",
|
||||
ReasonTTL: 45,
|
||||
},
|
||||
Scripts: []Script{
|
||||
{
|
||||
ID: "robots.txt",
|
||||
Output: "User-Agent: * /img/ /redir/ ",
|
||||
},
|
||||
{
|
||||
ID: "HTML title",
|
||||
Output: "freshmeat.net: Welcome to freshmeat.net",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: 443,
|
||||
Protocol: "tcp",
|
||||
Service: Service{
|
||||
Name: "https",
|
||||
Method: "table",
|
||||
Configuration: 3,
|
||||
},
|
||||
State: State{
|
||||
State: "closed",
|
||||
Reason: "reset",
|
||||
ReasonTTL: 46,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TaskBegin: []Task{
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479013, 0)),
|
||||
Task: "Ping Scan",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479014, 0)),
|
||||
Task: "Parallel DNS resolution of 8 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479015, 0)),
|
||||
Task: "System CNAME DNS resolution of 4 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479016, 0)),
|
||||
Task: "SYN Stealth Scan",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201480879, 0)),
|
||||
Task: "Service scan",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481006, 0)),
|
||||
Task: "Traceroute",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481028, 0)),
|
||||
Task: "Traceroute",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481059, 0)),
|
||||
Task: "Parallel DNS resolution of 85 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481070, 0)),
|
||||
Task: "System CNAME DNS resolution of 8 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481086, 0)),
|
||||
Task: "SCRIPT ENGINE",
|
||||
},
|
||||
},
|
||||
TaskProgress: []TaskProgress{
|
||||
{
|
||||
Percent: 3.22,
|
||||
Remaining: 903,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201479949, 0)),
|
||||
Time: Timestamp(time.Unix(1201479046, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 56.66,
|
||||
Remaining: 325,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201479767, 0)),
|
||||
Time: Timestamp(time.Unix(1201479442, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 77.02,
|
||||
Remaining: 225,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201479995, 0)),
|
||||
Time: Timestamp(time.Unix(1201479770, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 81.95,
|
||||
Remaining: 215,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480212, 0)),
|
||||
Time: Timestamp(time.Unix(1201479996, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 86.79,
|
||||
Remaining: 182,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480395, 0)),
|
||||
Time: Timestamp(time.Unix(1201480213, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 87.84,
|
||||
Remaining: 172,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480433, 0)),
|
||||
Time: Timestamp(time.Unix(1201480260, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 91.65,
|
||||
Remaining: 129,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480564, 0)),
|
||||
Time: Timestamp(time.Unix(1201480435, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 94.43,
|
||||
Remaining: 91,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480656, 0)),
|
||||
Time: Timestamp(time.Unix(1201480565, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 96.35,
|
||||
Remaining: 62,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480720, 0)),
|
||||
Time: Timestamp(time.Unix(1201480658, 0)),
|
||||
},
|
||||
{
|
||||
Percent: 97.76,
|
||||
Remaining: 39,
|
||||
Task: "SYN Stealth Scan",
|
||||
Etc: Timestamp(time.Unix(1201480760, 0)),
|
||||
Time: Timestamp(time.Unix(1201480721, 0)),
|
||||
},
|
||||
},
|
||||
TaskEnd: []Task{
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479014, 0)),
|
||||
Task: "Ping Scan",
|
||||
ExtraInfo: "8 total hosts",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479015, 0)),
|
||||
Task: "Parallel DNS resolution of 8 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201479016, 0)),
|
||||
Task: "System CNAME DNS resolution of 4 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201480878, 0)),
|
||||
Task: "SYN Stealth Scan",
|
||||
ExtraInfo: "8570 total ports",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201480984, 0)),
|
||||
Task: "Service scan",
|
||||
ExtraInfo: "20 services on 5 hosts",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481028, 0)),
|
||||
Task: "Traceroute",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481059, 0)),
|
||||
Task: "Traceroute",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481070, 0)),
|
||||
Task: "Parallel DNS resolution of 85 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481086, 0)),
|
||||
Task: "System CNAME DNS resolution of 8 hosts.",
|
||||
},
|
||||
{
|
||||
Time: Timestamp(time.Unix(1201481197, 0)),
|
||||
Task: "SCRIPT ENGINE",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.inputFile, func(t *testing.T) {
|
||||
rawXML, err := ioutil.ReadFile(test.inputFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, err := Parse(rawXML)
|
||||
|
||||
// Remove rawXML before comparing
|
||||
result.rawXML = []byte{}
|
||||
|
||||
compareResults(t, test.expectedResult, result)
|
||||
|
||||
if err != test.expectedError {
|
||||
t.Errorf("expected %v got %v", test.expectedError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func compareResults(t *testing.T, expected, got *Run) {
|
||||
if got.Args != expected.Args {
|
||||
t.Errorf("unexpected arguments, expected %v got %v", expected.Args, got.Args)
|
||||
}
|
||||
|
||||
if got.ProfileName != expected.ProfileName {
|
||||
t.Errorf("unexpected arguments, expected %v got %v", expected.ProfileName, got.ProfileName)
|
||||
}
|
||||
|
||||
if got.Scanner != expected.Scanner {
|
||||
t.Errorf("unexpected arguments, expected %v got %v", expected.Scanner, got.Scanner)
|
||||
}
|
||||
|
||||
if got.StartStr != expected.StartStr {
|
||||
t.Errorf("unexpected arguments, expected %v got %v", expected.StartStr, got.StartStr)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got.Debugging, expected.Debugging) {
|
||||
t.Errorf("unexpected debugging, expected %+v got %+v", expected.Debugging, got.Debugging)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got.ScanInfo, expected.ScanInfo) {
|
||||
t.Errorf("unexpected scan info, expected %+v got %+v", expected.ScanInfo, got.ScanInfo)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got.Start, expected.Start) {
|
||||
t.Errorf("unexpected start time, expected %+v got %+v", expected.Start, got.Start)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got.Targets, expected.Targets) {
|
||||
t.Errorf("unexpected targets, expected %+v got %+v", expected.Targets, got.Targets)
|
||||
}
|
||||
|
||||
if len(expected.TaskBegin) != len(got.TaskBegin) {
|
||||
t.Errorf("unexpected tasks begin entries, expected to have %d entries, got %d instead", len(expected.TaskBegin), len(got.TaskBegin))
|
||||
} else {
|
||||
for idx := range expected.TaskBegin {
|
||||
if !reflect.DeepEqual(got.TaskBegin[idx], expected.TaskBegin[idx]) {
|
||||
t.Errorf("unexpected task begin entry, expected %+v got %+v", expected.TaskBegin[idx], got.TaskBegin[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(expected.TaskProgress) != len(got.TaskProgress) {
|
||||
t.Errorf("unexpected tasks progress entries, expected to have %d entries, got %d instead", len(expected.TaskProgress), len(got.TaskProgress))
|
||||
} else {
|
||||
for idx := range expected.TaskProgress {
|
||||
if !reflect.DeepEqual(got.TaskProgress[idx], expected.TaskProgress[idx]) {
|
||||
t.Errorf("unexpected task progress entry, expected %+v got %+v", expected.TaskProgress[idx], got.TaskProgress[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(expected.TaskEnd) != len(got.TaskEnd) {
|
||||
t.Errorf("unexpected tasks end entries, expected to have %d entries, got %d instead", len(expected.TaskEnd), len(got.TaskEnd))
|
||||
} else {
|
||||
for idx := range expected.TaskEnd {
|
||||
if !reflect.DeepEqual(got.TaskEnd[idx], expected.TaskEnd[idx]) {
|
||||
t.Errorf("unexpected task end entry, expected %+v got %+v", expected.TaskEnd[idx], got.TaskEnd[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(expected.Hosts) != len(got.Hosts) {
|
||||
t.Errorf("unexpected number of hosts, expected to have %d hosts, got %d instead", len(expected.Hosts), len(got.Hosts))
|
||||
} else {
|
||||
for idx := range expected.Hosts {
|
||||
if expected.Hosts[idx].Comment != got.Hosts[idx].Comment {
|
||||
t.Errorf("unexpected host comment, expected %v got %v", expected.Hosts[idx].Comment, got.Hosts[idx].Comment)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Addresses, got.Hosts[idx].Addresses) {
|
||||
t.Errorf("unexpected host addresses, expected %+v got %+v", expected.Hosts[idx].Addresses, got.Hosts[idx].Addresses)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Distance, got.Hosts[idx].Distance) {
|
||||
t.Errorf("unexpected host distance, expected %+v got %+v", expected.Hosts[idx].Distance, got.Hosts[idx].Distance)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].ExtraPorts, got.Hosts[idx].ExtraPorts) {
|
||||
t.Errorf("unexpected host extra ports, expected %+v got %+v", expected.Hosts[idx].ExtraPorts, got.Hosts[idx].ExtraPorts)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].HostScripts, got.Hosts[idx].HostScripts) {
|
||||
t.Errorf("unexpected host host scripts, expected %+v got %+v", expected.Hosts[idx].HostScripts, got.Hosts[idx].HostScripts)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Hostnames, got.Hosts[idx].Hostnames) {
|
||||
t.Errorf("unexpected host host names, expected %+v got %+v", expected.Hosts[idx].Hostnames, got.Hosts[idx].Hostnames)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].IPIDSequence, got.Hosts[idx].IPIDSequence) {
|
||||
t.Errorf("unexpected host IPIDSequence, expected %+v got %+v", expected.Hosts[idx].IPIDSequence, got.Hosts[idx].IPIDSequence)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].OS, got.Hosts[idx].OS) {
|
||||
t.Errorf("unexpected host OS, expected %+v got %+v", expected.Hosts[idx].OS, got.Hosts[idx].OS)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Ports, got.Hosts[idx].Ports) {
|
||||
t.Errorf("unexpected host ports, expected %+v got %+v", expected.Hosts[idx].Ports, got.Hosts[idx].Ports)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Smurfs, got.Hosts[idx].Smurfs) {
|
||||
t.Errorf("unexpected host smurfs, expected %+v got %+v", expected.Hosts[idx].Smurfs, got.Hosts[idx].Smurfs)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].StartTime, got.Hosts[idx].StartTime) {
|
||||
t.Errorf("unexpected host start time, expected %+v got %+v", expected.Hosts[idx].StartTime, got.Hosts[idx].StartTime)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Status, got.Hosts[idx].Status) {
|
||||
t.Errorf("unexpected host status, expected %+v got %+v", expected.Hosts[idx].Status, got.Hosts[idx].Status)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].TCPSequence, got.Hosts[idx].TCPSequence) {
|
||||
t.Errorf("unexpected host TCPSequence, expected %+v got %+v", expected.Hosts[idx].TCPSequence, got.Hosts[idx].TCPSequence)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].TCPTSSequence, got.Hosts[idx].TCPTSSequence) {
|
||||
t.Errorf("unexpected host TCPTSSequence, expected %+v got %+v", expected.Hosts[idx].TCPTSSequence, got.Hosts[idx].TCPTSSequence)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Times, got.Hosts[idx].Times) {
|
||||
t.Errorf("unexpected host times, expected %+v got %+v", expected.Hosts[idx].Times, got.Hosts[idx].Times)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Trace, got.Hosts[idx].Trace) {
|
||||
t.Errorf("unexpected host trace, expected %+v got %+v", expected.Hosts[idx].Trace, got.Hosts[idx].Trace)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected.Hosts[idx].Uptime, got.Hosts[idx].Uptime) {
|
||||
t.Errorf("unexpected host uptime, expected %+v got %+v", expected.Hosts[idx].Uptime, got.Hosts[idx].Uptime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fingerprint = "SCAN(V=4.53%D=1/27%OT=80%CT=443%CU=%PV=N%G=N%TM=479D25ED%P=i686-pc-linux-gnu)\nSEQ(SP=F2%GCD=1%ISR=E9%TI=Z%TS=1C)\nOPS(O1=M5B4ST11NW0%O2=M5B4ST11NW0%O3=M5B4NNT11NW0%O4=M5B4ST11NW0%O5=M5B4ST11NW0%O6=M5B4ST11)\nWIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)\nECN(R=Y%DF=Y%TG=40%W=16D0%O=M5B4NNSNW0%CC=N%Q=)\nT1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)\nT2(R=N)\nT3(R=Y%DF=Y%TG=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW0%RD=0%Q=)\nT4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)\nT5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)\nT6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)\nT7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)\nU1(R=N)\nIE(R=N)\n"
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
*.6
|
||||
*.o
|
||||
src/_cgo_*
|
||||
src/_obj/
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.9
|
||||
- 1.8
|
||||
- 1.7
|
||||
- 1.6
|
||||
before_install:
|
||||
- sudo apt-get update -qq > apt-get.out 2>&1 || (cat apt-get.out && exit 1)
|
||||
install: go build -x -v
|
||||
script: go test -v
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- fledna@foxmail.com
|
||||
on_success: change
|
||||
on_failure: always
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
Copyright 2014 Shuyu Wang <andelf@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this project 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.
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
go-curl
|
||||
=======
|
||||
|
||||
[](http://travis-ci.org/andelf/go-curl)
|
||||
|
||||
my golang libcurl(curl) binding.
|
||||
|
||||
See more examples in ./examples/ directory~!
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
||||
go-curl is licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||
|
||||
Current Development Status
|
||||
--------------------------
|
||||
|
||||
* currently stable
|
||||
* READ, WRITE, HEADER, PROGRESS function callback
|
||||
* a Multipart Form supports file uploading
|
||||
* Most curl_easy_setopt option
|
||||
* partly implement share & multi interface
|
||||
* new callback function prototype
|
||||
|
||||
How to Install
|
||||
--------------
|
||||
|
||||
Make Sure You Have libcurl (and its develop headers, static/dynamic libs) installed!
|
||||
|
||||
|
||||
$ go get -u github.com/andelf/go-curl
|
||||
|
||||
Current Status
|
||||
--------------
|
||||
|
||||
* Linux x64
|
||||
* passed go1 (ArchLinux)
|
||||
* Windows x86
|
||||
* passed go1 (win7, mingw-gcc 4.5.2, curl 7.22.0)
|
||||
* Mac OS
|
||||
* passed go1 (Mac OS X 10.7.3, curl 7.21.4)
|
||||
|
||||
Sample Program
|
||||
--------------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
curl "github.com/andelf/go-curl"
|
||||
)
|
||||
|
||||
func main() {
|
||||
easy := curl.EasyInit()
|
||||
defer easy.Cleanup()
|
||||
|
||||
easy.Setopt(curl.OPT_URL, "http://www.baidu.com/")
|
||||
|
||||
// make a callback function
|
||||
fooTest := func (buf []byte, userdata interface{}) bool {
|
||||
println("DEBUG: size=>", len(buf))
|
||||
println("DEBUG: content=>", string(buf))
|
||||
return true
|
||||
}
|
||||
|
||||
easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
|
||||
|
||||
if err := easy.Perform(); err != nil {
|
||||
fmt.Printf("ERROR: %v\n", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "callback.h"
|
||||
#include "_cgo_export.h"
|
||||
|
||||
/* for OPT_HEADERFUNCTION */
|
||||
size_t header_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
|
||||
return goCallHeaderFunction(ptr, size*nmemb, ctx);
|
||||
}
|
||||
|
||||
void *return_header_function() {
|
||||
return (void *)&header_function;
|
||||
}
|
||||
|
||||
|
||||
/* for OPT_WRITEFUNCTION */
|
||||
size_t write_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
|
||||
return goCallWriteFunction(ptr, size*nmemb, ctx);
|
||||
}
|
||||
|
||||
void *return_write_function() {
|
||||
return (void *)&write_function;
|
||||
}
|
||||
|
||||
/* for OPT_READFUNCTION */
|
||||
size_t read_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
|
||||
return goCallReadFunction(ptr, size*nmemb, ctx);
|
||||
}
|
||||
|
||||
void *return_read_function() {
|
||||
return (void *)&read_function;
|
||||
}
|
||||
|
||||
|
||||
/* for OPT_PROGRESSFUNCTION */
|
||||
int progress_function(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow) {
|
||||
return goCallProgressFunction(dltotal, dlnow, ultotal, ulnow, ctx);
|
||||
}
|
||||
|
||||
void *return_progress_function() {
|
||||
return (void *)progress_function;
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package curl
|
||||
|
||||
/*
|
||||
#cgo freebsd CFLAGS: -I/usr/local/include
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/lib -lcurl
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//export goCallHeaderFunction
|
||||
func goCallHeaderFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
|
||||
curl := context_map.Get(uintptr(ctx))
|
||||
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
|
||||
if (*curl.headerFunction)(buf, curl.headerData) {
|
||||
return uintptr(size)
|
||||
}
|
||||
return C.CURL_WRITEFUNC_PAUSE
|
||||
}
|
||||
|
||||
//export goCallWriteFunction
|
||||
func goCallWriteFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
|
||||
curl := context_map.Get(uintptr(ctx))
|
||||
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
|
||||
if (*curl.writeFunction)(buf, curl.writeData) {
|
||||
return uintptr(size)
|
||||
}
|
||||
return C.CURL_WRITEFUNC_PAUSE
|
||||
}
|
||||
|
||||
//export goCallProgressFunction
|
||||
func goCallProgressFunction(dltotal, dlnow, ultotal, ulnow C.double, ctx unsafe.Pointer) int {
|
||||
curl := context_map.Get(uintptr(ctx))
|
||||
if (*curl.progressFunction)(float64(dltotal), float64(dlnow),
|
||||
float64(ultotal), float64(ulnow),
|
||||
curl.progressData) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
//export goCallReadFunction
|
||||
func goCallReadFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
|
||||
curl := context_map.Get(uintptr(ctx))
|
||||
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
|
||||
ret := (*curl.readFunction)(buf, curl.readData)
|
||||
str := C.CString(string(buf))
|
||||
defer C.free(unsafe.Pointer(str))
|
||||
if C.memcpy(unsafe.Pointer(ptr), unsafe.Pointer(str), C.size_t(ret)) == nil {
|
||||
panic("read_callback memcpy error!")
|
||||
}
|
||||
return uintptr(ret)
|
||||
}
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
|
||||
void *return_header_function();
|
||||
void *return_write_function();
|
||||
void *return_read_function();
|
||||
|
||||
void *return_progress_function();
|
||||
-528
@@ -1,528 +0,0 @@
|
||||
|
||||
/* generated by compatgen.py */
|
||||
#include<curl/curl.h>
|
||||
|
||||
|
||||
|
||||
#if (LIBCURL_VERSION_MINOR == 57 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 57
|
||||
#define CURL_VERSION_BROTLI 0
|
||||
#if (LIBCURL_VERSION_MINOR == 56 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 56
|
||||
#if (LIBCURL_VERSION_MINOR == 56 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 56
|
||||
#define CURLOPT_SSH_COMPRESSION 0
|
||||
#define CURLOPT_MIMEPOST 0
|
||||
#define CURL_VERSION_MULTI_SSL 0
|
||||
#if (LIBCURL_VERSION_MINOR == 55 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 55
|
||||
#if (LIBCURL_VERSION_MINOR == 55 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 55
|
||||
#define CURLOPT_REQUEST_TARGET 0
|
||||
#define CURLOPT_SOCKS5_AUTH 0
|
||||
#define CURLINFO_SIZE_UPLOAD_T 0
|
||||
#define CURLINFO_SIZE_DOWNLOAD_T 0
|
||||
#define CURLINFO_SPEED_DOWNLOAD_T 0
|
||||
#define CURLINFO_SPEED_UPLOAD_T 0
|
||||
#define CURLINFO_CONTENT_LENGTH_DOWNLOAD_T 0
|
||||
#define CURLINFO_CONTENT_LENGTH_UPLOAD_T 0
|
||||
#define CURLAUTH_GSSAPI 0
|
||||
#if (LIBCURL_VERSION_MINOR == 54 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 54
|
||||
#if (LIBCURL_VERSION_MINOR == 54 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 54
|
||||
#define CURLOPT_SUPPRESS_CONNECT_HEADERS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 53 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 53
|
||||
#if (LIBCURL_VERSION_MINOR == 53 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 53
|
||||
#define CURLOPT_ABSTRACT_UNIX_SOCKET 0
|
||||
#if (LIBCURL_VERSION_MINOR == 52 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 52
|
||||
#if (LIBCURL_VERSION_MINOR == 52 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 52
|
||||
#define CURLOPT_PROXY_CAINFO 0
|
||||
#define CURLOPT_PROXY_CAPATH 0
|
||||
#define CURLOPT_PROXY_SSL_VERIFYPEER 0
|
||||
#define CURLOPT_PROXY_SSL_VERIFYHOST 0
|
||||
#define CURLOPT_PROXY_SSLVERSION 0
|
||||
#define CURLOPT_PROXY_TLSAUTH_USERNAME 0
|
||||
#define CURLOPT_PROXY_TLSAUTH_PASSWORD 0
|
||||
#define CURLOPT_PROXY_TLSAUTH_TYPE 0
|
||||
#define CURLOPT_PROXY_SSLCERT 0
|
||||
#define CURLOPT_PROXY_SSLCERTTYPE 0
|
||||
#define CURLOPT_PROXY_SSLKEY 0
|
||||
#define CURLOPT_PROXY_SSLKEYTYPE 0
|
||||
#define CURLOPT_PROXY_KEYPASSWD 0
|
||||
#define CURLOPT_PROXY_SSL_CIPHER_LIST 0
|
||||
#define CURLOPT_PROXY_CRLFILE 0
|
||||
#define CURLOPT_PROXY_SSL_OPTIONS 0
|
||||
#define CURLOPT_PRE_PROXY 0
|
||||
#define CURLOPT_PROXY_PINNEDPUBLICKEY 0
|
||||
#define CURLINFO_PROXY_SSL_VERIFYRESULT 0
|
||||
#define CURLINFO_PROTOCOL 0
|
||||
#define CURLINFO_SCHEME 0
|
||||
#define CURL_VERSION_HTTPS_PROXY 0
|
||||
#if (LIBCURL_VERSION_MINOR == 51 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 51
|
||||
#define CURLOPT_KEEP_SENDING_ON_ERROR 0
|
||||
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 50
|
||||
#define CURLE_WEIRD_SERVER_REPLY -1
|
||||
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 50
|
||||
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 50
|
||||
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 50
|
||||
#define CURLINFO_HTTP_VERSION 0
|
||||
#if (LIBCURL_VERSION_MINOR == 49 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 49
|
||||
#if (LIBCURL_VERSION_MINOR == 49 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 49
|
||||
#define CURLOPT_CONNECT_TO 0
|
||||
#define CURLOPT_TCP_FASTOPEN 0
|
||||
#define CURLE_TOO_MANY_REDIRECTS -1
|
||||
#define CURLE_TELNET_OPTION_SYNTAX -1
|
||||
#define CURLE_HTTP2_STREAM -1
|
||||
#if (LIBCURL_VERSION_MINOR == 48 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 48
|
||||
#define CURLOPT_TFTP_NO_OPTIONS 0
|
||||
#define CURLINFO_TLS_SSL_PTR 0
|
||||
#if (LIBCURL_VERSION_MINOR == 47 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 47
|
||||
#if (LIBCURL_VERSION_MINOR == 47 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 47
|
||||
#define CURL_VERSION_PSL 0
|
||||
#if (LIBCURL_VERSION_MINOR == 46 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 46
|
||||
#define CURLOPT_STREAM_WEIGHT 0
|
||||
#define CURLOPT_STREAM_DEPENDS 0
|
||||
#define CURLOPT_STREAM_DEPENDS_E 0
|
||||
#if (LIBCURL_VERSION_MINOR == 45 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 45
|
||||
#define CURLOPT_DEFAULT_PROTOCOL 0
|
||||
#define CURLINFO_ACTIVESOCKET 0
|
||||
#if (LIBCURL_VERSION_MINOR == 44 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 44
|
||||
#if (LIBCURL_VERSION_MINOR == 43 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 43
|
||||
#define CURLOPT_PROXY_SERVICE_NAME 0
|
||||
#define CURLOPT_SERVICE_NAME 0
|
||||
#define CURLOPT_PIPEWAIT 0
|
||||
#if (LIBCURL_VERSION_MINOR == 42 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 42
|
||||
#if (LIBCURL_VERSION_MINOR == 42 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 42
|
||||
#define CURLOPT_SSL_FALSESTART 0
|
||||
#define CURLOPT_PATH_AS_IS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 41 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 41
|
||||
#define CURLOPT_SSL_VERIFYSTATUS 0
|
||||
#define CURLE_SSL_INVALIDCERTSTATUS -1
|
||||
#if (LIBCURL_VERSION_MINOR == 40 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 40
|
||||
#define CURLOPT_UNIX_SOCKET_PATH 0
|
||||
#define CURL_VERSION_KERBEROS5 0
|
||||
#define CURL_VERSION_UNIX_SOCKETS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 39 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 39
|
||||
#define CURLOPT_PINNEDPUBLICKEY 0
|
||||
#define CURLE_SSL_PINNEDPUBKEYNOTMATCH -1
|
||||
#if (LIBCURL_VERSION_MINOR == 38 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 38
|
||||
#define CURLE_HTTP2 -1
|
||||
#define CURL_VERSION_GSSAPI 0
|
||||
#define CURLAUTH_NEGOTIATE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 37 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 37
|
||||
#define CURLOPT_OBSOLETE40 0
|
||||
#define CURLOPT_OBSOLETE72 0
|
||||
#if (LIBCURL_VERSION_MINOR == 37 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 37
|
||||
#define CURLOPT_PROXYHEADER 0
|
||||
#define CURLOPT_HEADEROPT 0
|
||||
#if (LIBCURL_VERSION_MINOR == 36 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 36
|
||||
#define CURLOPT_SSL_ENABLE_NPN 0
|
||||
#define CURLOPT_SSL_ENABLE_ALPN 0
|
||||
#define CURLOPT_EXPECT_100_TIMEOUT_MS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 35 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 35
|
||||
#if (LIBCURL_VERSION_MINOR == 34 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 34
|
||||
#define CURLOPT_LOGIN_OPTIONS 0
|
||||
#define CURLINFO_TLS_SESSION 0
|
||||
#if (LIBCURL_VERSION_MINOR == 33 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 33
|
||||
#define CURLOPT_XOAUTH2_BEARER 0
|
||||
#define CURLOPT_DNS_INTERFACE 0
|
||||
#define CURLOPT_DNS_LOCAL_IP4 0
|
||||
#define CURLOPT_DNS_LOCAL_IP6 0
|
||||
#define CURL_VERSION_HTTP2 0
|
||||
#if (LIBCURL_VERSION_MINOR == 32 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 32
|
||||
#define CURLOPT_XFERINFODATA 0
|
||||
#define CURLOPT_XFERINFOFUNCTION 0
|
||||
#if (LIBCURL_VERSION_MINOR == 31 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 31
|
||||
#define CURLOPT_SASL_IR 0
|
||||
#if (LIBCURL_VERSION_MINOR == 30 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 30
|
||||
#define CURLE_NO_CONNECTION_AVAILABLE -1
|
||||
#if (LIBCURL_VERSION_MINOR == 29 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 29
|
||||
#if (LIBCURL_VERSION_MINOR == 28 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 28
|
||||
#if (LIBCURL_VERSION_MINOR == 28 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 28
|
||||
#if (LIBCURL_VERSION_MINOR == 27 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 27
|
||||
#if (LIBCURL_VERSION_MINOR == 26 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 26
|
||||
#if (LIBCURL_VERSION_MINOR == 25 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 25
|
||||
#define CURLOPT_TCP_KEEPALIVE 0
|
||||
#define CURLOPT_TCP_KEEPIDLE 0
|
||||
#define CURLOPT_TCP_KEEPINTVL 0
|
||||
#define CURLOPT_SSL_OPTIONS 0
|
||||
#define CURLOPT_MAIL_AUTH 0
|
||||
#if (LIBCURL_VERSION_MINOR == 24 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 24
|
||||
#define CURLOPT_DNS_SERVERS 0
|
||||
#define CURLOPT_ACCEPTTIMEOUT_MS 0
|
||||
#define CURLE_FTP_ACCEPT_FAILED -1
|
||||
#define CURLE_FTP_ACCEPT_TIMEOUT -1
|
||||
#if (LIBCURL_VERSION_MINOR == 23 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 23
|
||||
#if (LIBCURL_VERSION_MINOR == 23 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 23
|
||||
#if (LIBCURL_VERSION_MINOR == 22 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 22
|
||||
#define CURLOPT_GSSAPI_DELEGATION 0
|
||||
#define CURL_VERSION_NTLM_WB 0
|
||||
#define CURLAUTH_NTLM_WB 0
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLOPT_CLOSESOCKETFUNCTION 0
|
||||
#define CURLOPT_CLOSESOCKETDATA 0
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLOPT_ACCEPT_ENCODING 0
|
||||
#define CURLOPT_TRANSFER_ENCODING 0
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLE_NOT_BUILT_IN -1
|
||||
#define CURLE_UNKNOWN_OPTION -1
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLOPT_TLSAUTH_USERNAME 0
|
||||
#define CURLOPT_TLSAUTH_PASSWORD 0
|
||||
#define CURLOPT_TLSAUTH_TYPE 0
|
||||
#define CURL_VERSION_TLSAUTH_SRP 0
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLOPT_RESOLVE 0
|
||||
#define CURLAUTH_ONLY 0
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 21
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 21
|
||||
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 21
|
||||
#define CURLOPT_WILDCARDMATCH 0
|
||||
#define CURLOPT_CHUNK_BGN_FUNCTION 0
|
||||
#define CURLOPT_CHUNK_END_FUNCTION 0
|
||||
#define CURLOPT_FNMATCH_FUNCTION 0
|
||||
#define CURLOPT_CHUNK_DATA 0
|
||||
#define CURLOPT_FNMATCH_DATA 0
|
||||
#define CURLE_FTP_BAD_FILE_LIST -1
|
||||
#define CURLE_CHUNK_FAILED -1
|
||||
#define CURLINFO_PRIMARY_PORT 0
|
||||
#define CURLINFO_LOCAL_IP 0
|
||||
#define CURLINFO_LOCAL_PORT 0
|
||||
#if (LIBCURL_VERSION_MINOR == 20 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 20
|
||||
#if (LIBCURL_VERSION_MINOR == 20 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 20
|
||||
#define CURLOPT_SERVER_RESPONSE_TIMEOUT 0
|
||||
#define CURLOPT_MAIL_FROM 0
|
||||
#define CURLOPT_MAIL_RCPT 0
|
||||
#define CURLOPT_FTP_USE_PRET 0
|
||||
#define CURLOPT_RTSP_REQUEST 0
|
||||
#define CURLOPT_RTSP_SESSION_ID 0
|
||||
#define CURLOPT_RTSP_STREAM_URI 0
|
||||
#define CURLOPT_RTSP_TRANSPORT 0
|
||||
#define CURLOPT_RTSP_CLIENT_CSEQ 0
|
||||
#define CURLOPT_RTSP_SERVER_CSEQ 0
|
||||
#define CURLOPT_INTERLEAVEDATA 0
|
||||
#define CURLOPT_INTERLEAVEFUNCTION 0
|
||||
#define CURLOPT_RTSPHEADER 0
|
||||
#define CURLE_FTP_PRET_FAILED -1
|
||||
#define CURLE_RTSP_CSEQ_ERROR -1
|
||||
#define CURLE_RTSP_SESSION_ERROR -1
|
||||
#define CURLINFO_RTSP_SESSION_ID 0
|
||||
#define CURLINFO_RTSP_CLIENT_CSEQ 0
|
||||
#define CURLINFO_RTSP_SERVER_CSEQ 0
|
||||
#define CURLINFO_RTSP_CSEQ_RECV 0
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 19
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 19
|
||||
#define CURLOPT_SSH_KNOWNHOSTS 0
|
||||
#define CURLOPT_SSH_KEYFUNCTION 0
|
||||
#define CURLOPT_SSH_KEYDATA 0
|
||||
#define CURL_VERSION_CURLDEBUG 0
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 19
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 19
|
||||
#define CURLOPT_NOPROXY 0
|
||||
#define CURLOPT_TFTP_BLKSIZE 0
|
||||
#define CURLOPT_SOCKS5_GSSAPI_SERVICE 0
|
||||
#define CURLOPT_SOCKS5_GSSAPI_NEC 0
|
||||
#define CURLOPT_PROTOCOLS 0
|
||||
#define CURLOPT_REDIR_PROTOCOLS 0
|
||||
#define CURLINFO_CONDITION_UNMET 0
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 19
|
||||
#define CURLAUTH_DIGEST_IE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 19
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 19
|
||||
#define CURLOPT_POSTREDIR 0
|
||||
#define CURLOPT_CERTINFO 0
|
||||
#define CURLOPT_USERNAME 0
|
||||
#define CURLOPT_PASSWORD 0
|
||||
#define CURLOPT_PROXYUSERNAME 0
|
||||
#define CURLOPT_PROXYPASSWORD 0
|
||||
#define CURLINFO_CERTINFO 0
|
||||
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 19
|
||||
#define CURLOPT_CRLFILE 0
|
||||
#define CURLOPT_ISSUERCERT 0
|
||||
#define CURLOPT_ADDRESS_SCOPE 0
|
||||
#define CURLE_SSL_CRL_BADFILE -1
|
||||
#define CURLE_SSL_ISSUER_ERROR -1
|
||||
#define CURLINFO_PRIMARY_IP 0
|
||||
#define CURLINFO_APPCONNECT_TIME 0
|
||||
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 18
|
||||
#define CURLE_AGAIN -1
|
||||
#define CURLINFO_REDIRECT_URL 0
|
||||
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 18
|
||||
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 18
|
||||
#define CURLOPT_PROXY_TRANSFER_MODE 0
|
||||
#define CURLOPT_SEEKFUNCTION 0
|
||||
#define CURLOPT_SEEKDATA 0
|
||||
#if (LIBCURL_VERSION_MINOR == 17 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 17
|
||||
#define CURLOPT_POST301 0
|
||||
#define CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 0
|
||||
#define CURLOPT_OPENSOCKETFUNCTION 0
|
||||
#define CURLOPT_OPENSOCKETDATA 0
|
||||
#define CURLOPT_COPYPOSTFIELDS 0
|
||||
#define CURLE_PEER_FAILED_VERIFICATION -1
|
||||
#if (LIBCURL_VERSION_MINOR == 17 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 17
|
||||
#define CURLOPT_LOW_SPEED_LIMIT 0
|
||||
#define CURLOPT_KEYPASSWD 0
|
||||
#define CURLOPT_DIRLISTONLY 0
|
||||
#define CURLOPT_APPEND 0
|
||||
#define CURLOPT_FTP_RESPONSE_TIMEOUT 0
|
||||
#define CURLOPT_USE_SSL 0
|
||||
#define CURLE_OBSOLETE4 -1
|
||||
#define CURLE_REMOTE_ACCESS_DENIED -1
|
||||
#define CURLE_OBSOLETE10 -1
|
||||
#define CURLE_OBSOLETE12 -1
|
||||
#define CURLE_OBSOLETE16 -1
|
||||
#define CURLE_FTP_COULDNT_SET_TYPE -1
|
||||
#define CURLE_OBSOLETE20 -1
|
||||
#define CURLE_QUOTE_ERROR -1
|
||||
#define CURLE_OBSOLETE24 -1
|
||||
#define CURLE_OBSOLETE29 -1
|
||||
#define CURLE_OBSOLETE32 -1
|
||||
#define CURLE_RANGE_ERROR -1
|
||||
#define CURLE_OBSOLETE40 -1
|
||||
#define CURLE_OBSOLETE44 -1
|
||||
#define CURLE_OBSOLETE46 -1
|
||||
#define CURLE_OBSOLETE50 -1
|
||||
#define CURLE_OBSOLETE57 -1
|
||||
#define CURLE_USE_SSL_FAILED -1
|
||||
#define CURLE_REMOTE_DISK_FULL -1
|
||||
#define CURLE_REMOTE_FILE_EXISTS -1
|
||||
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 16
|
||||
#define CURLOPT_KRBLEVEL 0
|
||||
#define CURLOPT_NEW_FILE_PERMS 0
|
||||
#define CURLOPT_NEW_DIRECTORY_PERMS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 16
|
||||
#define CURLE_UPLOAD_FAILED -1
|
||||
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 16
|
||||
#define CURLOPT_TIMEOUT_MS 0
|
||||
#define CURLOPT_CONNECTTIMEOUT_MS 0
|
||||
#define CURLOPT_HTTP_TRANSFER_DECODING 0
|
||||
#define CURLOPT_HTTP_CONTENT_DECODING 0
|
||||
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 16
|
||||
#define CURLOPT_SSH_AUTH_TYPES 0
|
||||
#define CURLOPT_SSH_PUBLIC_KEYFILE 0
|
||||
#define CURLOPT_SSH_PRIVATE_KEYFILE 0
|
||||
#define CURLOPT_FTP_SSL_CCC 0
|
||||
#define CURLE_REMOTE_FILE_NOT_FOUND -1
|
||||
#define CURLE_SSH -1
|
||||
#define CURLE_SSL_SHUTDOWN_FAILED -1
|
||||
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 16
|
||||
#define CURLOPT_SOCKOPTFUNCTION 0
|
||||
#define CURLOPT_SOCKOPTDATA 0
|
||||
#define CURLOPT_SSL_SESSIONID_CACHE 0
|
||||
#define CURLE_SSL_CACERT_BADFILE -1
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 15
|
||||
#define CURLOPT_MAX_SEND_SPEED_LARGE 0
|
||||
#define CURLOPT_MAX_RECV_SPEED_LARGE 0
|
||||
#define CURLOPT_FTP_ALTERNATIVE_TO_USER 0
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 15
|
||||
#define CURLOPT_CONV_FROM_NETWORK_FUNCTION 0
|
||||
#define CURLOPT_CONV_TO_NETWORK_FUNCTION 0
|
||||
#define CURLOPT_CONV_FROM_UTF8_FUNCTION 0
|
||||
#define CURLE_CONV_FAILED -1
|
||||
#define CURLE_CONV_REQD -1
|
||||
#define CURLINFO_FTP_ENTRY_PATH 0
|
||||
#define CURL_VERSION_CONV 0
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 15
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 15
|
||||
#define CURLOPT_LOCALPORT 0
|
||||
#define CURLOPT_LOCALPORTRANGE 0
|
||||
#define CURLOPT_CONNECT_ONLY 0
|
||||
#define CURLINFO_LASTSOCKET 0
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 15
|
||||
#define CURLOPT_FTP_FILEMETHOD 0
|
||||
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 15
|
||||
#define CURLOPT_FTP_SKIP_PASV_IP 0
|
||||
#define CURLE_TFTP_NOTFOUND -1
|
||||
#define CURLE_TFTP_PERM -1
|
||||
#define CURLE_TFTP_DISKFULL -1
|
||||
#define CURLE_TFTP_ILLEGAL -1
|
||||
#define CURLE_TFTP_UNKNOWNID -1
|
||||
#define CURLE_TFTP_EXISTS -1
|
||||
#define CURLE_TFTP_NOSUCHUSER -1
|
||||
#if (LIBCURL_VERSION_MINOR == 14 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 14
|
||||
#define CURLOPT_COOKIELIST 0
|
||||
#define CURLOPT_IGNORE_CONTENT_LENGTH 0
|
||||
#define CURLINFO_COOKIELIST 0
|
||||
#if (LIBCURL_VERSION_MINOR == 14 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 14
|
||||
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 13
|
||||
#define CURL_VERSION_SSPI 0
|
||||
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 13
|
||||
#define CURLE_LOGIN_DENIED -1
|
||||
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 13
|
||||
#define CURLOPT_SOURCE_URL 0
|
||||
#define CURLOPT_SOURCE_QUOTE 0
|
||||
#define CURLOPT_FTP_ACCOUNT 0
|
||||
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 12
|
||||
#define CURLOPT_IOCTLFUNCTION 0
|
||||
#define CURLOPT_IOCTLDATA 0
|
||||
#define CURLE_SEND_FAIL_REWIND -1
|
||||
#define CURLE_SSL_ENGINE_INITFAILED -1
|
||||
#define CURLINFO_NUM_CONNECTS 0
|
||||
#define CURLINFO_SSL_ENGINES 0
|
||||
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 12
|
||||
#define CURLOPT_FTPSSLAUTH 0
|
||||
#define CURLINFO_OS_ERRNO 0
|
||||
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 12
|
||||
#define CURLOPT_SOURCE_HOST 0
|
||||
#define CURLOPT_SOURCE_USERPWD 0
|
||||
#define CURLOPT_SOURCE_PATH 0
|
||||
#define CURLOPT_SOURCE_PORT 0
|
||||
#define CURLOPT_PASV_HOST 0
|
||||
#define CURLOPT_SOURCE_PREQUOTE 0
|
||||
#define CURLOPT_SOURCE_POSTQUOTE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 12
|
||||
#define CURLE_INTERFACE_FAILED -1
|
||||
#define CURL_VERSION_IDN 0
|
||||
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 11
|
||||
#define CURLOPT_TCP_NODELAY 0
|
||||
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 11
|
||||
#define CURLOPT_POSTFIELDSIZE_LARGE 0
|
||||
#define CURL_VERSION_LARGEFILE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 11
|
||||
#define CURLOPT_INFILESIZE_LARGE 0
|
||||
#define CURLOPT_RESUME_FROM_LARGE 0
|
||||
#define CURLOPT_MAXFILESIZE_LARGE 0
|
||||
#define CURLOPT_NETRC_FILE 0
|
||||
#define CURLOPT_FTP_SSL 0
|
||||
#define CURLE_FTP_SSL_FAILED -1
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 8) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_IPRESOLVE 0
|
||||
#define CURLOPT_MAXFILESIZE 0
|
||||
#define CURLE_LDAP_INVALID_URL -1
|
||||
#define CURLE_FILESIZE_EXCEEDED -1
|
||||
#define CURLINFO_RESPONSE_CODE 0
|
||||
#define CURLINFO_HTTPAUTH_AVAIL 0
|
||||
#define CURLINFO_PROXYAUTH_AVAIL 0
|
||||
#define CURL_VERSION_SPNEGO 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_FTP_CREATE_MISSING_DIRS 0
|
||||
#define CURLOPT_PROXYAUTH 0
|
||||
#define CURLINFO_HTTP_CONNECTCODE 0
|
||||
#define CURL_VERSION_ASYNCHDNS 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_HTTPAUTH 0
|
||||
#define CURLOPT_SSL_CTX_FUNCTION 0
|
||||
#define CURLOPT_SSL_CTX_DATA 0
|
||||
#define CURL_VERSION_NTLM 0
|
||||
#define CURL_VERSION_GSSNEGOTIATE 0
|
||||
#define CURL_VERSION_DEBUG 0
|
||||
#define CURLAUTH_NONE 0
|
||||
#define CURLAUTH_BASIC 0
|
||||
#define CURLAUTH_DIGEST 0
|
||||
#define CURLAUTH_GSSNEGOTIATE 0
|
||||
#define CURLAUTH_NTLM 0
|
||||
#define CURLAUTH_ANY 0
|
||||
#define CURLAUTH_ANYSAFE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_FTP_USE_EPRT 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_UNRESTRICTED_AUTH 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLOPT_PRIVATE 0
|
||||
#define CURLOPT_HTTP200ALIASES 0
|
||||
#define CURLE_HTTP_RETURNED_ERROR -1
|
||||
#define CURLINFO_PRIVATE 0
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 10
|
||||
#define CURLE_OPERATION_TIMEDOUT -1
|
||||
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 10
|
||||
#error your version is TOOOOOOOO low
|
||||
#endif /* 7.10.1 */
|
||||
#endif /* 7.10.2 */
|
||||
#endif /* 7.10.3 */
|
||||
#endif /* 7.10.4 */
|
||||
#endif /* 7.10.5 */
|
||||
#endif /* 7.10.6 */
|
||||
#endif /* 7.10.7 */
|
||||
#endif /* 7.10.8 */
|
||||
#endif /* 7.11.0 */
|
||||
#endif /* 7.11.1 */
|
||||
#endif /* 7.11.2 */
|
||||
#endif /* 7.12.0 */
|
||||
#endif /* 7.12.1 */
|
||||
#endif /* 7.12.2 */
|
||||
#endif /* 7.12.3 */
|
||||
#endif /* 7.13.0 */
|
||||
#endif /* 7.13.1 */
|
||||
#endif /* 7.13.2 */
|
||||
#endif /* 7.14.0 */
|
||||
#endif /* 7.14.1 */
|
||||
#endif /* 7.15.0 */
|
||||
#endif /* 7.15.1 */
|
||||
#endif /* 7.15.2 */
|
||||
#endif /* 7.15.3 */
|
||||
#endif /* 7.15.4 */
|
||||
#endif /* 7.15.5 */
|
||||
#endif /* 7.16.0 */
|
||||
#endif /* 7.16.1 */
|
||||
#endif /* 7.16.2 */
|
||||
#endif /* 7.16.3 */
|
||||
#endif /* 7.16.4 */
|
||||
#endif /* 7.17.0 */
|
||||
#endif /* 7.17.1 */
|
||||
#endif /* 7.18.0 */
|
||||
#endif /* 7.18.1 */
|
||||
#endif /* 7.18.2 */
|
||||
#endif /* 7.19.0 */
|
||||
#endif /* 7.19.1 */
|
||||
#endif /* 7.19.2 */
|
||||
#endif /* 7.19.3 */
|
||||
#endif /* 7.19.4 */
|
||||
#endif /* 7.19.5 */
|
||||
#endif /* 7.19.6 */
|
||||
#endif /* 7.19.7 */
|
||||
#endif /* 7.20.0 */
|
||||
#endif /* 7.20.1 */
|
||||
#endif /* 7.21.0 */
|
||||
#endif /* 7.21.1 */
|
||||
#endif /* 7.21.2 */
|
||||
#endif /* 7.21.3 */
|
||||
#endif /* 7.21.4 */
|
||||
#endif /* 7.21.5 */
|
||||
#endif /* 7.21.6 */
|
||||
#endif /* 7.21.7 */
|
||||
#endif /* 7.22.0 */
|
||||
#endif /* 7.23.0 */
|
||||
#endif /* 7.23.1 */
|
||||
#endif /* 7.24.0 */
|
||||
#endif /* 7.25.0 */
|
||||
#endif /* 7.26.0 */
|
||||
#endif /* 7.27.0 */
|
||||
#endif /* 7.28.0 */
|
||||
#endif /* 7.28.1 */
|
||||
#endif /* 7.29.0 */
|
||||
#endif /* 7.30.0 */
|
||||
#endif /* 7.31.0 */
|
||||
#endif /* 7.32.0 */
|
||||
#endif /* 7.33.0 */
|
||||
#endif /* 7.34.0 */
|
||||
#endif /* 7.35.0 */
|
||||
#endif /* 7.36.0 */
|
||||
#endif /* 7.37.0 */
|
||||
#endif /* 7.37.1 */
|
||||
#endif /* 7.38.0 */
|
||||
#endif /* 7.39.0 */
|
||||
#endif /* 7.40.0 */
|
||||
#endif /* 7.41.0 */
|
||||
#endif /* 7.42.0 */
|
||||
#endif /* 7.42.1 */
|
||||
#endif /* 7.43.0 */
|
||||
#endif /* 7.44.0 */
|
||||
#endif /* 7.45.0 */
|
||||
#endif /* 7.46.0 */
|
||||
#endif /* 7.47.0 */
|
||||
#endif /* 7.47.1 */
|
||||
#endif /* 7.48.0 */
|
||||
#endif /* 7.49.0 */
|
||||
#endif /* 7.49.1 */
|
||||
#endif /* 7.50.0 */
|
||||
#endif /* 7.50.1 */
|
||||
#endif /* 7.50.2 */
|
||||
#endif /* 7.50.3 */
|
||||
#endif /* 7.51.0 */
|
||||
#endif /* 7.52.0 */
|
||||
#endif /* 7.52.1 */
|
||||
#endif /* 7.53.0 */
|
||||
#endif /* 7.53.1 */
|
||||
#endif /* 7.54.0 */
|
||||
#endif /* 7.54.1 */
|
||||
#endif /* 7.55.0 */
|
||||
#endif /* 7.55.1 */
|
||||
#endif /* 7.56.0 */
|
||||
#endif /* 7.56.1 */
|
||||
#endif /* 7.57.0 */
|
||||
/* generated ends */
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
package curl
|
||||
|
||||
/*
|
||||
#include <curl/curl.h>
|
||||
#include "compat.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// for GlobalInit(flag)
|
||||
const (
|
||||
GLOBAL_SSL = C.CURL_GLOBAL_SSL
|
||||
GLOBAL_WIN32 = C.CURL_GLOBAL_WIN32
|
||||
GLOBAL_ALL = C.CURL_GLOBAL_ALL
|
||||
GLOBAL_NOTHING = C.CURL_GLOBAL_NOTHING
|
||||
GLOBAL_DEFAULT = C.CURL_GLOBAL_DEFAULT
|
||||
)
|
||||
|
||||
// CURLMcode
|
||||
const (
|
||||
M_CALL_MULTI_PERFORM = C.CURLM_CALL_MULTI_PERFORM
|
||||
// M_OK = C.CURLM_OK
|
||||
M_BAD_HANDLE = C.CURLM_BAD_HANDLE
|
||||
M_BAD_EASY_HANDLE = C.CURLM_BAD_EASY_HANDLE
|
||||
M_OUT_OF_MEMORY = C.CURLM_OUT_OF_MEMORY
|
||||
M_INTERNAL_ERROR = C.CURLM_INTERNAL_ERROR
|
||||
M_BAD_SOCKET = C.CURLM_BAD_SOCKET
|
||||
M_UNKNOWN_OPTION = C.CURLM_UNKNOWN_OPTION
|
||||
)
|
||||
|
||||
// for multi.Setopt(flag, ...)
|
||||
const (
|
||||
MOPT_SOCKETFUNCTION = C.CURLMOPT_SOCKETFUNCTION
|
||||
MOPT_SOCKETDATA = C.CURLMOPT_SOCKETDATA
|
||||
MOPT_PIPELINING = C.CURLMOPT_PIPELINING
|
||||
MOPT_TIMERFUNCTION = C.CURLMOPT_TIMERFUNCTION
|
||||
MOPT_TIMERDATA = C.CURLMOPT_TIMERDATA
|
||||
MOPT_MAXCONNECTS = C.CURLMOPT_MAXCONNECTS
|
||||
)
|
||||
|
||||
// CURLSHcode
|
||||
const (
|
||||
// SHE_OK = C.CURLSHE_OK
|
||||
SHE_BAD_OPTION = C.CURLSHE_BAD_OPTION
|
||||
SHE_IN_USE = C.CURLSHE_IN_USE
|
||||
SHE_INVALID = C.CURLSHE_INVALID
|
||||
SHE_NOMEM = C.CURLSHE_NOMEM
|
||||
)
|
||||
|
||||
// for share.Setopt(flat, ...)
|
||||
const (
|
||||
SHOPT_SHARE = C.CURLSHOPT_SHARE
|
||||
SHOPT_UNSHARE = C.CURLSHOPT_UNSHARE
|
||||
SHOPT_LOCKFUNC = C.CURLSHOPT_LOCKFUNC
|
||||
SHOPT_UNLOCKFUNC = C.CURLSHOPT_UNLOCKFUNC
|
||||
SHOPT_USERDATA = C.CURLSHOPT_USERDATA
|
||||
)
|
||||
|
||||
// for share.Setopt(SHOPT_SHARE/SHOPT_UNSHARE, flag)
|
||||
const (
|
||||
LOCK_DATA_SHARE = C.CURL_LOCK_DATA_SHARE
|
||||
LOCK_DATA_COOKIE = C.CURL_LOCK_DATA_COOKIE
|
||||
LOCK_DATA_DNS = C.CURL_LOCK_DATA_DNS
|
||||
LOCK_DATA_SSL_SESSION = C.CURL_LOCK_DATA_SSL_SESSION
|
||||
LOCK_DATA_CONNECT = C.CURL_LOCK_DATA_CONNECT
|
||||
)
|
||||
|
||||
// for VersionInfo(flag)
|
||||
const (
|
||||
VERSION_FIRST = C.CURLVERSION_FIRST
|
||||
VERSION_SECOND = C.CURLVERSION_SECOND
|
||||
VERSION_THIRD = C.CURLVERSION_THIRD
|
||||
// VERSION_FOURTH = C.CURLVERSION_FOURTH
|
||||
VERSION_LAST = C.CURLVERSION_LAST
|
||||
VERSION_NOW = C.CURLVERSION_NOW
|
||||
)
|
||||
|
||||
// for VersionInfo(...).Features mask flag
|
||||
const (
|
||||
VERSION_IPV6 = C.CURL_VERSION_IPV6
|
||||
VERSION_KERBEROS4 = C.CURL_VERSION_KERBEROS4
|
||||
VERSION_SSL = C.CURL_VERSION_SSL
|
||||
VERSION_LIBZ = C.CURL_VERSION_LIBZ
|
||||
VERSION_NTLM = C.CURL_VERSION_NTLM
|
||||
VERSION_GSSNEGOTIATE = C.CURL_VERSION_GSSNEGOTIATE
|
||||
VERSION_DEBUG = C.CURL_VERSION_DEBUG
|
||||
VERSION_ASYNCHDNS = C.CURL_VERSION_ASYNCHDNS
|
||||
VERSION_SPNEGO = C.CURL_VERSION_SPNEGO
|
||||
VERSION_LARGEFILE = C.CURL_VERSION_LARGEFILE
|
||||
VERSION_IDN = C.CURL_VERSION_IDN
|
||||
VERSION_SSPI = C.CURL_VERSION_SSPI
|
||||
VERSION_CONV = C.CURL_VERSION_CONV
|
||||
VERSION_CURLDEBUG = C.CURL_VERSION_CURLDEBUG
|
||||
VERSION_TLSAUTH_SRP = C.CURL_VERSION_TLSAUTH_SRP
|
||||
VERSION_NTLM_WB = C.CURL_VERSION_NTLM_WB
|
||||
)
|
||||
|
||||
// for OPT_READFUNCTION, return a int flag
|
||||
const (
|
||||
READFUNC_ABORT = C.CURL_READFUNC_ABORT
|
||||
READFUNC_PAUSE = C.CURL_READFUNC_PAUSE
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_HTTP_VERSION, flag)
|
||||
const (
|
||||
HTTP_VERSION_NONE = C.CURL_HTTP_VERSION_NONE
|
||||
HTTP_VERSION_1_0 = C.CURL_HTTP_VERSION_1_0
|
||||
HTTP_VERSION_1_1 = C.CURL_HTTP_VERSION_1_1
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_PROXYTYPE, flag)
|
||||
const (
|
||||
PROXY_HTTP = C.CURLPROXY_HTTP /* added in 7.10, new in 7.19.4 default is to use CONNECT HTTP/1.1 */
|
||||
PROXY_HTTP_1_0 = C.CURLPROXY_HTTP_1_0 /* added in 7.19.4, force to use CONNECT HTTP/1.0 */
|
||||
PROXY_SOCKS4 = C.CURLPROXY_SOCKS4 /* support added in 7.15.2, enum existed already in 7.10 */
|
||||
PROXY_SOCKS5 = C.CURLPROXY_SOCKS5 /* added in 7.10 */
|
||||
PROXY_SOCKS4A = C.CURLPROXY_SOCKS4A /* added in 7.18.0 */
|
||||
// Use the SOCKS5 protocol but pass along the host name rather than the IP address.
|
||||
PROXY_SOCKS5_HOSTNAME = C.CURLPROXY_SOCKS5_HOSTNAME
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_SSLVERSION, flag)
|
||||
const (
|
||||
SSLVERSION_DEFAULT = C.CURL_SSLVERSION_DEFAULT
|
||||
SSLVERSION_TLSv1 = C.CURL_SSLVERSION_TLSv1
|
||||
SSLVERSION_SSLv2 = C.CURL_SSLVERSION_SSLv2
|
||||
SSLVERSION_SSLv3 = C.CURL_SSLVERSION_SSLv3
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_TIMECONDITION, flag)
|
||||
const (
|
||||
TIMECOND_NONE = C.CURL_TIMECOND_NONE
|
||||
TIMECOND_IFMODSINCE = C.CURL_TIMECOND_IFMODSINCE
|
||||
TIMECOND_IFUNMODSINCE = C.CURL_TIMECOND_IFUNMODSINCE
|
||||
TIMECOND_LASTMOD = C.CURL_TIMECOND_LASTMOD
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_NETRC, flag)
|
||||
const (
|
||||
NETRC_IGNORED = C.CURL_NETRC_IGNORED
|
||||
NETRC_OPTIONAL = C.CURL_NETRC_OPTIONAL
|
||||
NETRC_REQUIRED = C.CURL_NETRC_REQUIRED
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_FTP_CREATE_MISSING_DIRS, flag)
|
||||
const (
|
||||
FTP_CREATE_DIR_NONE = C.CURLFTP_CREATE_DIR_NONE
|
||||
FTP_CREATE_DIR = C.CURLFTP_CREATE_DIR
|
||||
FTP_CREATE_DIR_RETRY = C.CURLFTP_CREATE_DIR_RETRY
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_IPRESOLVE, flag)
|
||||
const (
|
||||
IPRESOLVE_WHATEVER = C.CURL_IPRESOLVE_WHATEVER
|
||||
IPRESOLVE_V4 = C.CURL_IPRESOLVE_V4
|
||||
IPRESOLVE_V6 = C.CURL_IPRESOLVE_V6
|
||||
)
|
||||
|
||||
// for easy.Setopt(OPT_SSL_OPTIONS, flag)
|
||||
const (
|
||||
SSLOPT_ALLOW_BEAST = 1
|
||||
)
|
||||
|
||||
// for easy.Pause(flat)
|
||||
const (
|
||||
PAUSE_RECV = C.CURLPAUSE_RECV
|
||||
PAUSE_RECV_CONT = C.CURLPAUSE_RECV_CONT
|
||||
PAUSE_SEND = C.CURLPAUSE_SEND
|
||||
PAUSE_SEND_CONT = C.CURLPAUSE_SEND_CONT
|
||||
PAUSE_ALL = C.CURLPAUSE_ALL
|
||||
PAUSE_CONT = C.CURLPAUSE_CONT
|
||||
)
|
||||
|
||||
// for multi.Info_read()
|
||||
const (
|
||||
CURLMSG_NONE = C.CURLMSG_NONE
|
||||
CURLMSG_DONE = C.CURLMSG_DONE
|
||||
CURLMSG_LAST = C.CURLMSG_LAST
|
||||
)
|
||||
-488
@@ -1,488 +0,0 @@
|
||||
//go:generate /usr/bin/env python ./misc/codegen.py
|
||||
|
||||
package curl
|
||||
/*
|
||||
#include <curl/curl.h>
|
||||
#include "compat.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// CURLcode
|
||||
const (
|
||||
E_UNSUPPORTED_PROTOCOL = C.CURLE_UNSUPPORTED_PROTOCOL
|
||||
E_FAILED_INIT = C.CURLE_FAILED_INIT
|
||||
E_URL_MALFORMAT = C.CURLE_URL_MALFORMAT
|
||||
E_NOT_BUILT_IN = C.CURLE_NOT_BUILT_IN
|
||||
E_COULDNT_RESOLVE_PROXY = C.CURLE_COULDNT_RESOLVE_PROXY
|
||||
E_COULDNT_RESOLVE_HOST = C.CURLE_COULDNT_RESOLVE_HOST
|
||||
E_COULDNT_CONNECT = C.CURLE_COULDNT_CONNECT
|
||||
E_WEIRD_SERVER_REPLY = C.CURLE_WEIRD_SERVER_REPLY
|
||||
E_REMOTE_ACCESS_DENIED = C.CURLE_REMOTE_ACCESS_DENIED
|
||||
E_FTP_ACCEPT_FAILED = C.CURLE_FTP_ACCEPT_FAILED
|
||||
E_FTP_WEIRD_PASS_REPLY = C.CURLE_FTP_WEIRD_PASS_REPLY
|
||||
E_FTP_ACCEPT_TIMEOUT = C.CURLE_FTP_ACCEPT_TIMEOUT
|
||||
E_FTP_WEIRD_PASV_REPLY = C.CURLE_FTP_WEIRD_PASV_REPLY
|
||||
E_FTP_WEIRD_227_FORMAT = C.CURLE_FTP_WEIRD_227_FORMAT
|
||||
E_FTP_CANT_GET_HOST = C.CURLE_FTP_CANT_GET_HOST
|
||||
E_HTTP2 = C.CURLE_HTTP2
|
||||
E_FTP_COULDNT_SET_TYPE = C.CURLE_FTP_COULDNT_SET_TYPE
|
||||
E_PARTIAL_FILE = C.CURLE_PARTIAL_FILE
|
||||
E_FTP_COULDNT_RETR_FILE = C.CURLE_FTP_COULDNT_RETR_FILE
|
||||
E_OBSOLETE20 = C.CURLE_OBSOLETE20
|
||||
E_QUOTE_ERROR = C.CURLE_QUOTE_ERROR
|
||||
E_HTTP_RETURNED_ERROR = C.CURLE_HTTP_RETURNED_ERROR
|
||||
E_WRITE_ERROR = C.CURLE_WRITE_ERROR
|
||||
E_OBSOLETE24 = C.CURLE_OBSOLETE24
|
||||
E_UPLOAD_FAILED = C.CURLE_UPLOAD_FAILED
|
||||
E_READ_ERROR = C.CURLE_READ_ERROR
|
||||
E_OUT_OF_MEMORY = C.CURLE_OUT_OF_MEMORY
|
||||
E_OPERATION_TIMEDOUT = C.CURLE_OPERATION_TIMEDOUT
|
||||
E_OBSOLETE29 = C.CURLE_OBSOLETE29
|
||||
E_FTP_PORT_FAILED = C.CURLE_FTP_PORT_FAILED
|
||||
E_FTP_COULDNT_USE_REST = C.CURLE_FTP_COULDNT_USE_REST
|
||||
E_OBSOLETE32 = C.CURLE_OBSOLETE32
|
||||
E_RANGE_ERROR = C.CURLE_RANGE_ERROR
|
||||
E_HTTP_POST_ERROR = C.CURLE_HTTP_POST_ERROR
|
||||
E_SSL_CONNECT_ERROR = C.CURLE_SSL_CONNECT_ERROR
|
||||
E_BAD_DOWNLOAD_RESUME = C.CURLE_BAD_DOWNLOAD_RESUME
|
||||
E_FILE_COULDNT_READ_FILE = C.CURLE_FILE_COULDNT_READ_FILE
|
||||
E_LDAP_CANNOT_BIND = C.CURLE_LDAP_CANNOT_BIND
|
||||
E_LDAP_SEARCH_FAILED = C.CURLE_LDAP_SEARCH_FAILED
|
||||
E_OBSOLETE40 = C.CURLE_OBSOLETE40
|
||||
E_FUNCTION_NOT_FOUND = C.CURLE_FUNCTION_NOT_FOUND
|
||||
E_ABORTED_BY_CALLBACK = C.CURLE_ABORTED_BY_CALLBACK
|
||||
E_BAD_FUNCTION_ARGUMENT = C.CURLE_BAD_FUNCTION_ARGUMENT
|
||||
E_OBSOLETE44 = C.CURLE_OBSOLETE44
|
||||
E_INTERFACE_FAILED = C.CURLE_INTERFACE_FAILED
|
||||
E_OBSOLETE46 = C.CURLE_OBSOLETE46
|
||||
E_TOO_MANY_REDIRECTS = C.CURLE_TOO_MANY_REDIRECTS
|
||||
E_UNKNOWN_OPTION = C.CURLE_UNKNOWN_OPTION
|
||||
E_TELNET_OPTION_SYNTAX = C.CURLE_TELNET_OPTION_SYNTAX
|
||||
E_OBSOLETE50 = C.CURLE_OBSOLETE50
|
||||
E_PEER_FAILED_VERIFICATION = C.CURLE_PEER_FAILED_VERIFICATION
|
||||
E_GOT_NOTHING = C.CURLE_GOT_NOTHING
|
||||
E_SSL_ENGINE_NOTFOUND = C.CURLE_SSL_ENGINE_NOTFOUND
|
||||
E_SSL_ENGINE_SETFAILED = C.CURLE_SSL_ENGINE_SETFAILED
|
||||
E_SEND_ERROR = C.CURLE_SEND_ERROR
|
||||
E_RECV_ERROR = C.CURLE_RECV_ERROR
|
||||
E_OBSOLETE57 = C.CURLE_OBSOLETE57
|
||||
E_SSL_CERTPROBLEM = C.CURLE_SSL_CERTPROBLEM
|
||||
E_SSL_CIPHER = C.CURLE_SSL_CIPHER
|
||||
E_SSL_CACERT = C.CURLE_SSL_CACERT
|
||||
E_BAD_CONTENT_ENCODING = C.CURLE_BAD_CONTENT_ENCODING
|
||||
E_LDAP_INVALID_URL = C.CURLE_LDAP_INVALID_URL
|
||||
E_FILESIZE_EXCEEDED = C.CURLE_FILESIZE_EXCEEDED
|
||||
E_USE_SSL_FAILED = C.CURLE_USE_SSL_FAILED
|
||||
E_SEND_FAIL_REWIND = C.CURLE_SEND_FAIL_REWIND
|
||||
E_SSL_ENGINE_INITFAILED = C.CURLE_SSL_ENGINE_INITFAILED
|
||||
E_LOGIN_DENIED = C.CURLE_LOGIN_DENIED
|
||||
E_TFTP_NOTFOUND = C.CURLE_TFTP_NOTFOUND
|
||||
E_TFTP_PERM = C.CURLE_TFTP_PERM
|
||||
E_REMOTE_DISK_FULL = C.CURLE_REMOTE_DISK_FULL
|
||||
E_TFTP_ILLEGAL = C.CURLE_TFTP_ILLEGAL
|
||||
E_TFTP_UNKNOWNID = C.CURLE_TFTP_UNKNOWNID
|
||||
E_REMOTE_FILE_EXISTS = C.CURLE_REMOTE_FILE_EXISTS
|
||||
E_TFTP_NOSUCHUSER = C.CURLE_TFTP_NOSUCHUSER
|
||||
E_CONV_FAILED = C.CURLE_CONV_FAILED
|
||||
E_CONV_REQD = C.CURLE_CONV_REQD
|
||||
E_SSL_CACERT_BADFILE = C.CURLE_SSL_CACERT_BADFILE
|
||||
E_REMOTE_FILE_NOT_FOUND = C.CURLE_REMOTE_FILE_NOT_FOUND
|
||||
E_SSH = C.CURLE_SSH
|
||||
E_SSL_SHUTDOWN_FAILED = C.CURLE_SSL_SHUTDOWN_FAILED
|
||||
E_AGAIN = C.CURLE_AGAIN
|
||||
E_SSL_CRL_BADFILE = C.CURLE_SSL_CRL_BADFILE
|
||||
E_SSL_ISSUER_ERROR = C.CURLE_SSL_ISSUER_ERROR
|
||||
E_FTP_PRET_FAILED = C.CURLE_FTP_PRET_FAILED
|
||||
E_RTSP_CSEQ_ERROR = C.CURLE_RTSP_CSEQ_ERROR
|
||||
E_RTSP_SESSION_ERROR = C.CURLE_RTSP_SESSION_ERROR
|
||||
E_FTP_BAD_FILE_LIST = C.CURLE_FTP_BAD_FILE_LIST
|
||||
E_CHUNK_FAILED = C.CURLE_CHUNK_FAILED
|
||||
E_NO_CONNECTION_AVAILABLE = C.CURLE_NO_CONNECTION_AVAILABLE
|
||||
E_SSL_PINNEDPUBKEYNOTMATCH = C.CURLE_SSL_PINNEDPUBKEYNOTMATCH
|
||||
E_SSL_INVALIDCERTSTATUS = C.CURLE_SSL_INVALIDCERTSTATUS
|
||||
E_HTTP2_STREAM = C.CURLE_HTTP2_STREAM
|
||||
E_OBSOLETE16 = C.CURLE_OBSOLETE16
|
||||
E_OBSOLETE10 = C.CURLE_OBSOLETE10
|
||||
E_OBSOLETE12 = C.CURLE_OBSOLETE12
|
||||
E_FTP_WEIRD_SERVER_REPLY = C.CURLE_FTP_WEIRD_SERVER_REPLY
|
||||
E_UNKNOWN_TELNET_OPTION = C.CURLE_UNKNOWN_TELNET_OPTION
|
||||
E_SSL_PEER_CERTIFICATE = C.CURLE_SSL_PEER_CERTIFICATE
|
||||
E_OBSOLETE = C.CURLE_OBSOLETE
|
||||
E_BAD_PASSWORD_ENTERED = C.CURLE_BAD_PASSWORD_ENTERED
|
||||
E_BAD_CALLING_ORDER = C.CURLE_BAD_CALLING_ORDER
|
||||
E_FTP_USER_PASSWORD_INCORRECT = C.CURLE_FTP_USER_PASSWORD_INCORRECT
|
||||
E_FTP_CANT_RECONNECT = C.CURLE_FTP_CANT_RECONNECT
|
||||
E_FTP_COULDNT_GET_SIZE = C.CURLE_FTP_COULDNT_GET_SIZE
|
||||
E_FTP_COULDNT_SET_ASCII = C.CURLE_FTP_COULDNT_SET_ASCII
|
||||
E_FTP_WEIRD_USER_REPLY = C.CURLE_FTP_WEIRD_USER_REPLY
|
||||
E_FTP_WRITE_ERROR = C.CURLE_FTP_WRITE_ERROR
|
||||
E_LIBRARY_NOT_FOUND = C.CURLE_LIBRARY_NOT_FOUND
|
||||
E_MALFORMAT_USER = C.CURLE_MALFORMAT_USER
|
||||
E_SHARE_IN_USE = C.CURLE_SHARE_IN_USE
|
||||
E_URL_MALFORMAT_USER = C.CURLE_URL_MALFORMAT_USER
|
||||
E_FTP_ACCESS_DENIED = C.CURLE_FTP_ACCESS_DENIED
|
||||
E_FTP_COULDNT_SET_BINARY = C.CURLE_FTP_COULDNT_SET_BINARY
|
||||
E_FTP_QUOTE_ERROR = C.CURLE_FTP_QUOTE_ERROR
|
||||
E_TFTP_DISKFULL = C.CURLE_TFTP_DISKFULL
|
||||
E_TFTP_EXISTS = C.CURLE_TFTP_EXISTS
|
||||
E_HTTP_RANGE_ERROR = C.CURLE_HTTP_RANGE_ERROR
|
||||
E_FTP_SSL_FAILED = C.CURLE_FTP_SSL_FAILED
|
||||
E_OPERATION_TIMEOUTED = C.CURLE_OPERATION_TIMEOUTED
|
||||
E_HTTP_NOT_FOUND = C.CURLE_HTTP_NOT_FOUND
|
||||
E_HTTP_PORT_FAILED = C.CURLE_HTTP_PORT_FAILED
|
||||
E_FTP_COULDNT_STOR_FILE = C.CURLE_FTP_COULDNT_STOR_FILE
|
||||
E_FTP_PARTIAL_FILE = C.CURLE_FTP_PARTIAL_FILE
|
||||
E_FTP_BAD_DOWNLOAD_RESUME = C.CURLE_FTP_BAD_DOWNLOAD_RESUME
|
||||
E_ALREADY_COMPLETE = C.CURLE_ALREADY_COMPLETE
|
||||
)
|
||||
|
||||
// easy.Setopt(flag, ...)
|
||||
const (
|
||||
OPT_ENCODING = C.CURLOPT_ENCODING
|
||||
OPT_FILE = C.CURLOPT_FILE
|
||||
OPT_INFILE = C.CURLOPT_INFILE
|
||||
OPT_WRITEHEADER = C.CURLOPT_WRITEHEADER
|
||||
OPT_WRITEINFO = C.CURLOPT_WRITEINFO
|
||||
OPT_CLOSEPOLICY = C.CURLOPT_CLOSEPOLICY
|
||||
OPT_WRITEDATA = C.CURLOPT_WRITEDATA
|
||||
OPT_URL = C.CURLOPT_URL
|
||||
OPT_PORT = C.CURLOPT_PORT
|
||||
OPT_PROXY = C.CURLOPT_PROXY
|
||||
OPT_USERPWD = C.CURLOPT_USERPWD
|
||||
OPT_PROXYUSERPWD = C.CURLOPT_PROXYUSERPWD
|
||||
OPT_RANGE = C.CURLOPT_RANGE
|
||||
OPT_READDATA = C.CURLOPT_READDATA
|
||||
OPT_ERRORBUFFER = C.CURLOPT_ERRORBUFFER
|
||||
OPT_WRITEFUNCTION = C.CURLOPT_WRITEFUNCTION
|
||||
OPT_READFUNCTION = C.CURLOPT_READFUNCTION
|
||||
OPT_TIMEOUT = C.CURLOPT_TIMEOUT
|
||||
OPT_INFILESIZE = C.CURLOPT_INFILESIZE
|
||||
OPT_POSTFIELDS = C.CURLOPT_POSTFIELDS
|
||||
OPT_REFERER = C.CURLOPT_REFERER
|
||||
OPT_FTPPORT = C.CURLOPT_FTPPORT
|
||||
OPT_USERAGENT = C.CURLOPT_USERAGENT
|
||||
OPT_LOW_SPEED_LIMIT = C.CURLOPT_LOW_SPEED_LIMIT
|
||||
OPT_LOW_SPEED_TIME = C.CURLOPT_LOW_SPEED_TIME
|
||||
OPT_RESUME_FROM = C.CURLOPT_RESUME_FROM
|
||||
OPT_COOKIE = C.CURLOPT_COOKIE
|
||||
OPT_HTTPHEADER = C.CURLOPT_HTTPHEADER
|
||||
OPT_HTTPPOST = C.CURLOPT_HTTPPOST
|
||||
OPT_SSLCERT = C.CURLOPT_SSLCERT
|
||||
OPT_KEYPASSWD = C.CURLOPT_KEYPASSWD
|
||||
OPT_CRLF = C.CURLOPT_CRLF
|
||||
OPT_QUOTE = C.CURLOPT_QUOTE
|
||||
OPT_HEADERDATA = C.CURLOPT_HEADERDATA
|
||||
OPT_COOKIEFILE = C.CURLOPT_COOKIEFILE
|
||||
OPT_SSLVERSION = C.CURLOPT_SSLVERSION
|
||||
OPT_TIMECONDITION = C.CURLOPT_TIMECONDITION
|
||||
OPT_TIMEVALUE = C.CURLOPT_TIMEVALUE
|
||||
OPT_CUSTOMREQUEST = C.CURLOPT_CUSTOMREQUEST
|
||||
OPT_STDERR = C.CURLOPT_STDERR
|
||||
OPT_POSTQUOTE = C.CURLOPT_POSTQUOTE
|
||||
OPT_OBSOLETE40 = C.CURLOPT_OBSOLETE40
|
||||
OPT_VERBOSE = C.CURLOPT_VERBOSE
|
||||
OPT_HEADER = C.CURLOPT_HEADER
|
||||
OPT_NOPROGRESS = C.CURLOPT_NOPROGRESS
|
||||
OPT_NOBODY = C.CURLOPT_NOBODY
|
||||
OPT_FAILONERROR = C.CURLOPT_FAILONERROR
|
||||
OPT_UPLOAD = C.CURLOPT_UPLOAD
|
||||
OPT_POST = C.CURLOPT_POST
|
||||
OPT_DIRLISTONLY = C.CURLOPT_DIRLISTONLY
|
||||
OPT_APPEND = C.CURLOPT_APPEND
|
||||
OPT_NETRC = C.CURLOPT_NETRC
|
||||
OPT_FOLLOWLOCATION = C.CURLOPT_FOLLOWLOCATION
|
||||
OPT_TRANSFERTEXT = C.CURLOPT_TRANSFERTEXT
|
||||
OPT_PUT = C.CURLOPT_PUT
|
||||
OPT_PROGRESSFUNCTION = C.CURLOPT_PROGRESSFUNCTION
|
||||
OPT_PROGRESSDATA = C.CURLOPT_PROGRESSDATA
|
||||
OPT_XFERINFODATA = C.CURLOPT_XFERINFODATA
|
||||
OPT_AUTOREFERER = C.CURLOPT_AUTOREFERER
|
||||
OPT_PROXYPORT = C.CURLOPT_PROXYPORT
|
||||
OPT_POSTFIELDSIZE = C.CURLOPT_POSTFIELDSIZE
|
||||
OPT_HTTPPROXYTUNNEL = C.CURLOPT_HTTPPROXYTUNNEL
|
||||
OPT_INTERFACE = C.CURLOPT_INTERFACE
|
||||
OPT_KRBLEVEL = C.CURLOPT_KRBLEVEL
|
||||
OPT_SSL_VERIFYPEER = C.CURLOPT_SSL_VERIFYPEER
|
||||
OPT_CAINFO = C.CURLOPT_CAINFO
|
||||
OPT_MAXREDIRS = C.CURLOPT_MAXREDIRS
|
||||
OPT_FILETIME = C.CURLOPT_FILETIME
|
||||
OPT_TELNETOPTIONS = C.CURLOPT_TELNETOPTIONS
|
||||
OPT_MAXCONNECTS = C.CURLOPT_MAXCONNECTS
|
||||
OPT_OBSOLETE72 = C.CURLOPT_OBSOLETE72
|
||||
OPT_FRESH_CONNECT = C.CURLOPT_FRESH_CONNECT
|
||||
OPT_FORBID_REUSE = C.CURLOPT_FORBID_REUSE
|
||||
OPT_RANDOM_FILE = C.CURLOPT_RANDOM_FILE
|
||||
OPT_EGDSOCKET = C.CURLOPT_EGDSOCKET
|
||||
OPT_CONNECTTIMEOUT = C.CURLOPT_CONNECTTIMEOUT
|
||||
OPT_HEADERFUNCTION = C.CURLOPT_HEADERFUNCTION
|
||||
OPT_HTTPGET = C.CURLOPT_HTTPGET
|
||||
OPT_SSL_VERIFYHOST = C.CURLOPT_SSL_VERIFYHOST
|
||||
OPT_COOKIEJAR = C.CURLOPT_COOKIEJAR
|
||||
OPT_SSL_CIPHER_LIST = C.CURLOPT_SSL_CIPHER_LIST
|
||||
OPT_HTTP_VERSION = C.CURLOPT_HTTP_VERSION
|
||||
OPT_FTP_USE_EPSV = C.CURLOPT_FTP_USE_EPSV
|
||||
OPT_SSLCERTTYPE = C.CURLOPT_SSLCERTTYPE
|
||||
OPT_SSLKEY = C.CURLOPT_SSLKEY
|
||||
OPT_SSLKEYTYPE = C.CURLOPT_SSLKEYTYPE
|
||||
OPT_SSLENGINE = C.CURLOPT_SSLENGINE
|
||||
OPT_SSLENGINE_DEFAULT = C.CURLOPT_SSLENGINE_DEFAULT
|
||||
OPT_DNS_USE_GLOBAL_CACHE = C.CURLOPT_DNS_USE_GLOBAL_CACHE
|
||||
OPT_DNS_CACHE_TIMEOUT = C.CURLOPT_DNS_CACHE_TIMEOUT
|
||||
OPT_PREQUOTE = C.CURLOPT_PREQUOTE
|
||||
OPT_DEBUGFUNCTION = C.CURLOPT_DEBUGFUNCTION
|
||||
OPT_DEBUGDATA = C.CURLOPT_DEBUGDATA
|
||||
OPT_COOKIESESSION = C.CURLOPT_COOKIESESSION
|
||||
OPT_CAPATH = C.CURLOPT_CAPATH
|
||||
OPT_BUFFERSIZE = C.CURLOPT_BUFFERSIZE
|
||||
OPT_NOSIGNAL = C.CURLOPT_NOSIGNAL
|
||||
OPT_SHARE = C.CURLOPT_SHARE
|
||||
OPT_PROXYTYPE = C.CURLOPT_PROXYTYPE
|
||||
OPT_ACCEPT_ENCODING = C.CURLOPT_ACCEPT_ENCODING
|
||||
OPT_PRIVATE = C.CURLOPT_PRIVATE
|
||||
OPT_HTTP200ALIASES = C.CURLOPT_HTTP200ALIASES
|
||||
OPT_UNRESTRICTED_AUTH = C.CURLOPT_UNRESTRICTED_AUTH
|
||||
OPT_FTP_USE_EPRT = C.CURLOPT_FTP_USE_EPRT
|
||||
OPT_HTTPAUTH = C.CURLOPT_HTTPAUTH
|
||||
OPT_SSL_CTX_FUNCTION = C.CURLOPT_SSL_CTX_FUNCTION
|
||||
OPT_SSL_CTX_DATA = C.CURLOPT_SSL_CTX_DATA
|
||||
OPT_FTP_CREATE_MISSING_DIRS = C.CURLOPT_FTP_CREATE_MISSING_DIRS
|
||||
OPT_PROXYAUTH = C.CURLOPT_PROXYAUTH
|
||||
OPT_FTP_RESPONSE_TIMEOUT = C.CURLOPT_FTP_RESPONSE_TIMEOUT
|
||||
OPT_SERVER_RESPONSE_TIMEOUT = C.CURLOPT_SERVER_RESPONSE_TIMEOUT
|
||||
OPT_IPRESOLVE = C.CURLOPT_IPRESOLVE
|
||||
OPT_MAXFILESIZE = C.CURLOPT_MAXFILESIZE
|
||||
OPT_INFILESIZE_LARGE = C.CURLOPT_INFILESIZE_LARGE
|
||||
OPT_RESUME_FROM_LARGE = C.CURLOPT_RESUME_FROM_LARGE
|
||||
OPT_MAXFILESIZE_LARGE = C.CURLOPT_MAXFILESIZE_LARGE
|
||||
OPT_NETRC_FILE = C.CURLOPT_NETRC_FILE
|
||||
OPT_USE_SSL = C.CURLOPT_USE_SSL
|
||||
OPT_POSTFIELDSIZE_LARGE = C.CURLOPT_POSTFIELDSIZE_LARGE
|
||||
OPT_TCP_NODELAY = C.CURLOPT_TCP_NODELAY
|
||||
OPT_FTPSSLAUTH = C.CURLOPT_FTPSSLAUTH
|
||||
OPT_IOCTLFUNCTION = C.CURLOPT_IOCTLFUNCTION
|
||||
OPT_IOCTLDATA = C.CURLOPT_IOCTLDATA
|
||||
OPT_FTP_ACCOUNT = C.CURLOPT_FTP_ACCOUNT
|
||||
OPT_COOKIELIST = C.CURLOPT_COOKIELIST
|
||||
OPT_IGNORE_CONTENT_LENGTH = C.CURLOPT_IGNORE_CONTENT_LENGTH
|
||||
OPT_FTP_SKIP_PASV_IP = C.CURLOPT_FTP_SKIP_PASV_IP
|
||||
OPT_FTP_FILEMETHOD = C.CURLOPT_FTP_FILEMETHOD
|
||||
OPT_LOCALPORT = C.CURLOPT_LOCALPORT
|
||||
OPT_LOCALPORTRANGE = C.CURLOPT_LOCALPORTRANGE
|
||||
OPT_CONNECT_ONLY = C.CURLOPT_CONNECT_ONLY
|
||||
OPT_CONV_FROM_NETWORK_FUNCTION = C.CURLOPT_CONV_FROM_NETWORK_FUNCTION
|
||||
OPT_CONV_TO_NETWORK_FUNCTION = C.CURLOPT_CONV_TO_NETWORK_FUNCTION
|
||||
OPT_CONV_FROM_UTF8_FUNCTION = C.CURLOPT_CONV_FROM_UTF8_FUNCTION
|
||||
OPT_MAX_SEND_SPEED_LARGE = C.CURLOPT_MAX_SEND_SPEED_LARGE
|
||||
OPT_MAX_RECV_SPEED_LARGE = C.CURLOPT_MAX_RECV_SPEED_LARGE
|
||||
OPT_FTP_ALTERNATIVE_TO_USER = C.CURLOPT_FTP_ALTERNATIVE_TO_USER
|
||||
OPT_SOCKOPTFUNCTION = C.CURLOPT_SOCKOPTFUNCTION
|
||||
OPT_SOCKOPTDATA = C.CURLOPT_SOCKOPTDATA
|
||||
OPT_SSL_SESSIONID_CACHE = C.CURLOPT_SSL_SESSIONID_CACHE
|
||||
OPT_SSH_AUTH_TYPES = C.CURLOPT_SSH_AUTH_TYPES
|
||||
OPT_SSH_PUBLIC_KEYFILE = C.CURLOPT_SSH_PUBLIC_KEYFILE
|
||||
OPT_SSH_PRIVATE_KEYFILE = C.CURLOPT_SSH_PRIVATE_KEYFILE
|
||||
OPT_FTP_SSL_CCC = C.CURLOPT_FTP_SSL_CCC
|
||||
OPT_TIMEOUT_MS = C.CURLOPT_TIMEOUT_MS
|
||||
OPT_CONNECTTIMEOUT_MS = C.CURLOPT_CONNECTTIMEOUT_MS
|
||||
OPT_HTTP_TRANSFER_DECODING = C.CURLOPT_HTTP_TRANSFER_DECODING
|
||||
OPT_HTTP_CONTENT_DECODING = C.CURLOPT_HTTP_CONTENT_DECODING
|
||||
OPT_NEW_FILE_PERMS = C.CURLOPT_NEW_FILE_PERMS
|
||||
OPT_NEW_DIRECTORY_PERMS = C.CURLOPT_NEW_DIRECTORY_PERMS
|
||||
OPT_POSTREDIR = C.CURLOPT_POSTREDIR
|
||||
OPT_SSH_HOST_PUBLIC_KEY_MD5 = C.CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
|
||||
OPT_OPENSOCKETFUNCTION = C.CURLOPT_OPENSOCKETFUNCTION
|
||||
OPT_OPENSOCKETDATA = C.CURLOPT_OPENSOCKETDATA
|
||||
OPT_COPYPOSTFIELDS = C.CURLOPT_COPYPOSTFIELDS
|
||||
OPT_PROXY_TRANSFER_MODE = C.CURLOPT_PROXY_TRANSFER_MODE
|
||||
OPT_SEEKFUNCTION = C.CURLOPT_SEEKFUNCTION
|
||||
OPT_SEEKDATA = C.CURLOPT_SEEKDATA
|
||||
OPT_CRLFILE = C.CURLOPT_CRLFILE
|
||||
OPT_ISSUERCERT = C.CURLOPT_ISSUERCERT
|
||||
OPT_ADDRESS_SCOPE = C.CURLOPT_ADDRESS_SCOPE
|
||||
OPT_CERTINFO = C.CURLOPT_CERTINFO
|
||||
OPT_USERNAME = C.CURLOPT_USERNAME
|
||||
OPT_PASSWORD = C.CURLOPT_PASSWORD
|
||||
OPT_PROXYUSERNAME = C.CURLOPT_PROXYUSERNAME
|
||||
OPT_PROXYPASSWORD = C.CURLOPT_PROXYPASSWORD
|
||||
OPT_NOPROXY = C.CURLOPT_NOPROXY
|
||||
OPT_TFTP_BLKSIZE = C.CURLOPT_TFTP_BLKSIZE
|
||||
OPT_SOCKS5_GSSAPI_SERVICE = C.CURLOPT_SOCKS5_GSSAPI_SERVICE
|
||||
OPT_SOCKS5_GSSAPI_NEC = C.CURLOPT_SOCKS5_GSSAPI_NEC
|
||||
OPT_PROTOCOLS = C.CURLOPT_PROTOCOLS
|
||||
OPT_REDIR_PROTOCOLS = C.CURLOPT_REDIR_PROTOCOLS
|
||||
OPT_SSH_KNOWNHOSTS = C.CURLOPT_SSH_KNOWNHOSTS
|
||||
OPT_SSH_KEYFUNCTION = C.CURLOPT_SSH_KEYFUNCTION
|
||||
OPT_SSH_KEYDATA = C.CURLOPT_SSH_KEYDATA
|
||||
OPT_MAIL_FROM = C.CURLOPT_MAIL_FROM
|
||||
OPT_MAIL_RCPT = C.CURLOPT_MAIL_RCPT
|
||||
OPT_FTP_USE_PRET = C.CURLOPT_FTP_USE_PRET
|
||||
OPT_RTSP_REQUEST = C.CURLOPT_RTSP_REQUEST
|
||||
OPT_RTSP_SESSION_ID = C.CURLOPT_RTSP_SESSION_ID
|
||||
OPT_RTSP_STREAM_URI = C.CURLOPT_RTSP_STREAM_URI
|
||||
OPT_RTSP_TRANSPORT = C.CURLOPT_RTSP_TRANSPORT
|
||||
OPT_RTSP_CLIENT_CSEQ = C.CURLOPT_RTSP_CLIENT_CSEQ
|
||||
OPT_RTSP_SERVER_CSEQ = C.CURLOPT_RTSP_SERVER_CSEQ
|
||||
OPT_INTERLEAVEDATA = C.CURLOPT_INTERLEAVEDATA
|
||||
OPT_INTERLEAVEFUNCTION = C.CURLOPT_INTERLEAVEFUNCTION
|
||||
OPT_WILDCARDMATCH = C.CURLOPT_WILDCARDMATCH
|
||||
OPT_CHUNK_BGN_FUNCTION = C.CURLOPT_CHUNK_BGN_FUNCTION
|
||||
OPT_CHUNK_END_FUNCTION = C.CURLOPT_CHUNK_END_FUNCTION
|
||||
OPT_FNMATCH_FUNCTION = C.CURLOPT_FNMATCH_FUNCTION
|
||||
OPT_CHUNK_DATA = C.CURLOPT_CHUNK_DATA
|
||||
OPT_FNMATCH_DATA = C.CURLOPT_FNMATCH_DATA
|
||||
OPT_RESOLVE = C.CURLOPT_RESOLVE
|
||||
OPT_TLSAUTH_USERNAME = C.CURLOPT_TLSAUTH_USERNAME
|
||||
OPT_TLSAUTH_PASSWORD = C.CURLOPT_TLSAUTH_PASSWORD
|
||||
OPT_TLSAUTH_TYPE = C.CURLOPT_TLSAUTH_TYPE
|
||||
OPT_TRANSFER_ENCODING = C.CURLOPT_TRANSFER_ENCODING
|
||||
OPT_CLOSESOCKETFUNCTION = C.CURLOPT_CLOSESOCKETFUNCTION
|
||||
OPT_CLOSESOCKETDATA = C.CURLOPT_CLOSESOCKETDATA
|
||||
OPT_GSSAPI_DELEGATION = C.CURLOPT_GSSAPI_DELEGATION
|
||||
OPT_DNS_SERVERS = C.CURLOPT_DNS_SERVERS
|
||||
OPT_ACCEPTTIMEOUT_MS = C.CURLOPT_ACCEPTTIMEOUT_MS
|
||||
OPT_TCP_KEEPALIVE = C.CURLOPT_TCP_KEEPALIVE
|
||||
OPT_TCP_KEEPIDLE = C.CURLOPT_TCP_KEEPIDLE
|
||||
OPT_TCP_KEEPINTVL = C.CURLOPT_TCP_KEEPINTVL
|
||||
OPT_SSL_OPTIONS = C.CURLOPT_SSL_OPTIONS
|
||||
OPT_MAIL_AUTH = C.CURLOPT_MAIL_AUTH
|
||||
OPT_SASL_IR = C.CURLOPT_SASL_IR
|
||||
OPT_XFERINFOFUNCTION = C.CURLOPT_XFERINFOFUNCTION
|
||||
OPT_XOAUTH2_BEARER = C.CURLOPT_XOAUTH2_BEARER
|
||||
OPT_DNS_INTERFACE = C.CURLOPT_DNS_INTERFACE
|
||||
OPT_DNS_LOCAL_IP4 = C.CURLOPT_DNS_LOCAL_IP4
|
||||
OPT_DNS_LOCAL_IP6 = C.CURLOPT_DNS_LOCAL_IP6
|
||||
OPT_LOGIN_OPTIONS = C.CURLOPT_LOGIN_OPTIONS
|
||||
OPT_SSL_ENABLE_NPN = C.CURLOPT_SSL_ENABLE_NPN
|
||||
OPT_SSL_ENABLE_ALPN = C.CURLOPT_SSL_ENABLE_ALPN
|
||||
OPT_EXPECT_100_TIMEOUT_MS = C.CURLOPT_EXPECT_100_TIMEOUT_MS
|
||||
OPT_PROXYHEADER = C.CURLOPT_PROXYHEADER
|
||||
OPT_HEADEROPT = C.CURLOPT_HEADEROPT
|
||||
OPT_PINNEDPUBLICKEY = C.CURLOPT_PINNEDPUBLICKEY
|
||||
OPT_UNIX_SOCKET_PATH = C.CURLOPT_UNIX_SOCKET_PATH
|
||||
OPT_SSL_VERIFYSTATUS = C.CURLOPT_SSL_VERIFYSTATUS
|
||||
OPT_SSL_FALSESTART = C.CURLOPT_SSL_FALSESTART
|
||||
OPT_PATH_AS_IS = C.CURLOPT_PATH_AS_IS
|
||||
OPT_PROXY_SERVICE_NAME = C.CURLOPT_PROXY_SERVICE_NAME
|
||||
OPT_SERVICE_NAME = C.CURLOPT_SERVICE_NAME
|
||||
OPT_PIPEWAIT = C.CURLOPT_PIPEWAIT
|
||||
OPT_DEFAULT_PROTOCOL = C.CURLOPT_DEFAULT_PROTOCOL
|
||||
OPT_STREAM_WEIGHT = C.CURLOPT_STREAM_WEIGHT
|
||||
OPT_STREAM_DEPENDS = C.CURLOPT_STREAM_DEPENDS
|
||||
OPT_STREAM_DEPENDS_E = C.CURLOPT_STREAM_DEPENDS_E
|
||||
OPT_TFTP_NO_OPTIONS = C.CURLOPT_TFTP_NO_OPTIONS
|
||||
OPT_CONNECT_TO = C.CURLOPT_CONNECT_TO
|
||||
OPT_TCP_FASTOPEN = C.CURLOPT_TCP_FASTOPEN
|
||||
OPT_KEEP_SENDING_ON_ERROR = C.CURLOPT_KEEP_SENDING_ON_ERROR
|
||||
OPT_PROXY_CAINFO = C.CURLOPT_PROXY_CAINFO
|
||||
OPT_PROXY_CAPATH = C.CURLOPT_PROXY_CAPATH
|
||||
OPT_PROXY_SSL_VERIFYPEER = C.CURLOPT_PROXY_SSL_VERIFYPEER
|
||||
OPT_PROXY_SSL_VERIFYHOST = C.CURLOPT_PROXY_SSL_VERIFYHOST
|
||||
OPT_PROXY_SSLVERSION = C.CURLOPT_PROXY_SSLVERSION
|
||||
OPT_PROXY_TLSAUTH_USERNAME = C.CURLOPT_PROXY_TLSAUTH_USERNAME
|
||||
OPT_PROXY_TLSAUTH_PASSWORD = C.CURLOPT_PROXY_TLSAUTH_PASSWORD
|
||||
OPT_PROXY_TLSAUTH_TYPE = C.CURLOPT_PROXY_TLSAUTH_TYPE
|
||||
OPT_PROXY_SSLCERT = C.CURLOPT_PROXY_SSLCERT
|
||||
OPT_PROXY_SSLCERTTYPE = C.CURLOPT_PROXY_SSLCERTTYPE
|
||||
OPT_PROXY_SSLKEY = C.CURLOPT_PROXY_SSLKEY
|
||||
OPT_PROXY_SSLKEYTYPE = C.CURLOPT_PROXY_SSLKEYTYPE
|
||||
OPT_PROXY_KEYPASSWD = C.CURLOPT_PROXY_KEYPASSWD
|
||||
OPT_PROXY_SSL_CIPHER_LIST = C.CURLOPT_PROXY_SSL_CIPHER_LIST
|
||||
OPT_PROXY_CRLFILE = C.CURLOPT_PROXY_CRLFILE
|
||||
OPT_PROXY_SSL_OPTIONS = C.CURLOPT_PROXY_SSL_OPTIONS
|
||||
OPT_PRE_PROXY = C.CURLOPT_PRE_PROXY
|
||||
OPT_PROXY_PINNEDPUBLICKEY = C.CURLOPT_PROXY_PINNEDPUBLICKEY
|
||||
OPT_ABSTRACT_UNIX_SOCKET = C.CURLOPT_ABSTRACT_UNIX_SOCKET
|
||||
OPT_SUPPRESS_CONNECT_HEADERS = C.CURLOPT_SUPPRESS_CONNECT_HEADERS
|
||||
OPT_REQUEST_TARGET = C.CURLOPT_REQUEST_TARGET
|
||||
OPT_SOCKS5_AUTH = C.CURLOPT_SOCKS5_AUTH
|
||||
OPT_SSH_COMPRESSION = C.CURLOPT_SSH_COMPRESSION
|
||||
OPT_MIMEPOST = C.CURLOPT_MIMEPOST
|
||||
OPT_POST301 = C.CURLOPT_POST301
|
||||
OPT_SSLKEYPASSWD = C.CURLOPT_SSLKEYPASSWD
|
||||
OPT_FTPAPPEND = C.CURLOPT_FTPAPPEND
|
||||
OPT_FTPLISTONLY = C.CURLOPT_FTPLISTONLY
|
||||
OPT_FTP_SSL = C.CURLOPT_FTP_SSL
|
||||
OPT_SSLCERTPASSWD = C.CURLOPT_SSLCERTPASSWD
|
||||
OPT_KRB4LEVEL = C.CURLOPT_KRB4LEVEL
|
||||
OPT_RTSPHEADER = C.CURLOPT_RTSPHEADER
|
||||
)
|
||||
|
||||
// easy.Getinfo(flag)
|
||||
const (
|
||||
INFO_TEXT = C.CURLINFO_TEXT
|
||||
INFO_EFFECTIVE_URL = C.CURLINFO_EFFECTIVE_URL
|
||||
INFO_RESPONSE_CODE = C.CURLINFO_RESPONSE_CODE
|
||||
INFO_TOTAL_TIME = C.CURLINFO_TOTAL_TIME
|
||||
INFO_NAMELOOKUP_TIME = C.CURLINFO_NAMELOOKUP_TIME
|
||||
INFO_CONNECT_TIME = C.CURLINFO_CONNECT_TIME
|
||||
INFO_PRETRANSFER_TIME = C.CURLINFO_PRETRANSFER_TIME
|
||||
INFO_SIZE_UPLOAD = C.CURLINFO_SIZE_UPLOAD
|
||||
INFO_SIZE_UPLOAD_T = C.CURLINFO_SIZE_UPLOAD_T
|
||||
INFO_SIZE_DOWNLOAD = C.CURLINFO_SIZE_DOWNLOAD
|
||||
INFO_SIZE_DOWNLOAD_T = C.CURLINFO_SIZE_DOWNLOAD_T
|
||||
INFO_SPEED_DOWNLOAD = C.CURLINFO_SPEED_DOWNLOAD
|
||||
INFO_SPEED_DOWNLOAD_T = C.CURLINFO_SPEED_DOWNLOAD_T
|
||||
INFO_SPEED_UPLOAD = C.CURLINFO_SPEED_UPLOAD
|
||||
INFO_SPEED_UPLOAD_T = C.CURLINFO_SPEED_UPLOAD_T
|
||||
INFO_HEADER_SIZE = C.CURLINFO_HEADER_SIZE
|
||||
INFO_REQUEST_SIZE = C.CURLINFO_REQUEST_SIZE
|
||||
INFO_SSL_VERIFYRESULT = C.CURLINFO_SSL_VERIFYRESULT
|
||||
INFO_FILETIME = C.CURLINFO_FILETIME
|
||||
INFO_CONTENT_LENGTH_DOWNLOAD = C.CURLINFO_CONTENT_LENGTH_DOWNLOAD
|
||||
INFO_CONTENT_LENGTH_DOWNLOAD_T = C.CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
|
||||
INFO_CONTENT_LENGTH_UPLOAD = C.CURLINFO_CONTENT_LENGTH_UPLOAD
|
||||
INFO_CONTENT_LENGTH_UPLOAD_T = C.CURLINFO_CONTENT_LENGTH_UPLOAD_T
|
||||
INFO_STARTTRANSFER_TIME = C.CURLINFO_STARTTRANSFER_TIME
|
||||
INFO_CONTENT_TYPE = C.CURLINFO_CONTENT_TYPE
|
||||
INFO_REDIRECT_TIME = C.CURLINFO_REDIRECT_TIME
|
||||
INFO_REDIRECT_COUNT = C.CURLINFO_REDIRECT_COUNT
|
||||
INFO_PRIVATE = C.CURLINFO_PRIVATE
|
||||
INFO_HTTP_CONNECTCODE = C.CURLINFO_HTTP_CONNECTCODE
|
||||
INFO_HTTPAUTH_AVAIL = C.CURLINFO_HTTPAUTH_AVAIL
|
||||
INFO_PROXYAUTH_AVAIL = C.CURLINFO_PROXYAUTH_AVAIL
|
||||
INFO_OS_ERRNO = C.CURLINFO_OS_ERRNO
|
||||
INFO_NUM_CONNECTS = C.CURLINFO_NUM_CONNECTS
|
||||
INFO_SSL_ENGINES = C.CURLINFO_SSL_ENGINES
|
||||
INFO_COOKIELIST = C.CURLINFO_COOKIELIST
|
||||
INFO_LASTSOCKET = C.CURLINFO_LASTSOCKET
|
||||
INFO_FTP_ENTRY_PATH = C.CURLINFO_FTP_ENTRY_PATH
|
||||
INFO_REDIRECT_URL = C.CURLINFO_REDIRECT_URL
|
||||
INFO_PRIMARY_IP = C.CURLINFO_PRIMARY_IP
|
||||
INFO_APPCONNECT_TIME = C.CURLINFO_APPCONNECT_TIME
|
||||
INFO_CERTINFO = C.CURLINFO_CERTINFO
|
||||
INFO_CONDITION_UNMET = C.CURLINFO_CONDITION_UNMET
|
||||
INFO_RTSP_SESSION_ID = C.CURLINFO_RTSP_SESSION_ID
|
||||
INFO_RTSP_CLIENT_CSEQ = C.CURLINFO_RTSP_CLIENT_CSEQ
|
||||
INFO_RTSP_SERVER_CSEQ = C.CURLINFO_RTSP_SERVER_CSEQ
|
||||
INFO_RTSP_CSEQ_RECV = C.CURLINFO_RTSP_CSEQ_RECV
|
||||
INFO_PRIMARY_PORT = C.CURLINFO_PRIMARY_PORT
|
||||
INFO_LOCAL_IP = C.CURLINFO_LOCAL_IP
|
||||
INFO_LOCAL_PORT = C.CURLINFO_LOCAL_PORT
|
||||
INFO_TLS_SESSION = C.CURLINFO_TLS_SESSION
|
||||
INFO_ACTIVESOCKET = C.CURLINFO_ACTIVESOCKET
|
||||
INFO_TLS_SSL_PTR = C.CURLINFO_TLS_SSL_PTR
|
||||
INFO_HTTP_VERSION = C.CURLINFO_HTTP_VERSION
|
||||
INFO_PROXY_SSL_VERIFYRESULT = C.CURLINFO_PROXY_SSL_VERIFYRESULT
|
||||
INFO_PROTOCOL = C.CURLINFO_PROTOCOL
|
||||
INFO_SCHEME = C.CURLINFO_SCHEME
|
||||
INFO_LASTONE = C.CURLINFO_LASTONE
|
||||
INFO_HTTP_CODE = C.CURLINFO_HTTP_CODE
|
||||
)
|
||||
|
||||
// Auth
|
||||
const (
|
||||
AUTH_NONE = C.CURLAUTH_NONE & (1<<32 - 1)
|
||||
AUTH_BASIC = C.CURLAUTH_BASIC & (1<<32 - 1)
|
||||
AUTH_DIGEST = C.CURLAUTH_DIGEST & (1<<32 - 1)
|
||||
AUTH_NEGOTIATE = C.CURLAUTH_NEGOTIATE & (1<<32 - 1)
|
||||
AUTH_GSSNEGOTIATE = C.CURLAUTH_GSSNEGOTIATE & (1<<32 - 1)
|
||||
AUTH_GSSAPI = C.CURLAUTH_GSSAPI & (1<<32 - 1)
|
||||
AUTH_NTLM = C.CURLAUTH_NTLM & (1<<32 - 1)
|
||||
AUTH_DIGEST_IE = C.CURLAUTH_DIGEST_IE & (1<<32 - 1)
|
||||
AUTH_NTLM_WB = C.CURLAUTH_NTLM_WB & (1<<32 - 1)
|
||||
AUTH_ONLY = C.CURLAUTH_ONLY & (1<<32 - 1)
|
||||
AUTH_ANY = C.CURLAUTH_ANY & (1<<32 - 1)
|
||||
AUTH_ANYSAFE = C.CURLAUTH_ANYSAFE & (1<<32 - 1)
|
||||
)
|
||||
|
||||
// generated ends
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
// libcurl go bingding
|
||||
package curl
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: libcurl
|
||||
#cgo darwin LDFLAGS: -lcurl
|
||||
#cgo windows LDFLAGS: -lcurl
|
||||
#include <stdlib.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
static char *string_array_index(char **p, int i) {
|
||||
return p[i];
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// curl_global_init - Global libcurl initialisation
|
||||
func GlobalInit(flags int) error {
|
||||
return newCurlError(C.curl_global_init(C.long(flags)))
|
||||
}
|
||||
|
||||
// curl_global_cleanup - global libcurl cleanup
|
||||
func GlobalCleanup() {
|
||||
C.curl_global_cleanup()
|
||||
}
|
||||
|
||||
type VersionInfoData struct {
|
||||
Age C.CURLversion
|
||||
// age >= 0
|
||||
Version string
|
||||
VersionNum uint
|
||||
Host string
|
||||
Features int
|
||||
SslVersion string
|
||||
SslVersionNum int
|
||||
LibzVersion string
|
||||
Protocols []string
|
||||
// age >= 1
|
||||
Ares string
|
||||
AresNum int
|
||||
// age >= 2
|
||||
Libidn string
|
||||
// age >= 3
|
||||
IconvVerNum int
|
||||
LibsshVersion string
|
||||
}
|
||||
|
||||
// curl_version - returns the libcurl version string
|
||||
func Version() string {
|
||||
return C.GoString(C.curl_version())
|
||||
}
|
||||
|
||||
// curl_version_info - returns run-time libcurl version info
|
||||
func VersionInfo(ver C.CURLversion) *VersionInfoData {
|
||||
data := C.curl_version_info(ver)
|
||||
ret := new(VersionInfoData)
|
||||
ret.Age = data.age
|
||||
switch age := ret.Age; {
|
||||
case age >= 0:
|
||||
ret.Version = string(C.GoString(data.version))
|
||||
ret.VersionNum = uint(data.version_num)
|
||||
ret.Host = C.GoString(data.host)
|
||||
ret.Features = int(data.features)
|
||||
ret.SslVersion = C.GoString(data.ssl_version)
|
||||
ret.SslVersionNum = int(data.ssl_version_num)
|
||||
ret.LibzVersion = C.GoString(data.libz_version)
|
||||
// ugly but works
|
||||
ret.Protocols = []string{}
|
||||
for i := C.int(0); C.string_array_index(data.protocols, i) != nil; i++ {
|
||||
p := C.string_array_index(data.protocols, i)
|
||||
ret.Protocols = append(ret.Protocols, C.GoString(p))
|
||||
}
|
||||
fallthrough
|
||||
case age >= 1:
|
||||
ret.Ares = C.GoString(data.ares)
|
||||
ret.AresNum = int(data.ares_num)
|
||||
fallthrough
|
||||
case age >= 2:
|
||||
ret.Libidn = C.GoString(data.libidn)
|
||||
fallthrough
|
||||
case age >= 3:
|
||||
ret.IconvVerNum = int(data.iconv_ver_num)
|
||||
ret.LibsshVersion = C.GoString(data.libssh_version)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// curl_getdate - Convert a date string to number of seconds since January 1, 1970
|
||||
// In golang, we convert it to a *time.Time
|
||||
func Getdate(date string) *time.Time {
|
||||
datestr := C.CString(date)
|
||||
defer C.free(unsafe.Pointer(datestr))
|
||||
t := C.curl_getdate(datestr, nil)
|
||||
if t == -1 {
|
||||
return nil
|
||||
}
|
||||
unix := time.Unix(int64(t), 0).UTC()
|
||||
return &unix
|
||||
|
||||
/*
|
||||
// curl_getenv - return value for environment name
|
||||
func Getenv(name string) string {
|
||||
namestr := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(namestr))
|
||||
ret := C.curl_getenv(unsafe.Pointer(namestr))
|
||||
defer C.free(unsafe.Pointer(ret))
|
||||
|
||||
return C.GoString(ret)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// TODO: curl_global_init_mem
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package curl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersionInfo(t *testing.T) {
|
||||
info := VersionInfo(VERSION_FIRST)
|
||||
expectedProtocols := []string{"dict", "file", "ftp", "ftps", "gopher", "http", "https", "imap", "imaps", "ldap", "ldaps", "pop3", "pop3s", "rtmp", "rtsp", "smtp", "smtps", "telnet", "tftp", "scp", "sftp", "smb", "smbs"}
|
||||
protocols := info.Protocols
|
||||
for _, protocol := range protocols {
|
||||
found := false
|
||||
for _, expectedProtocol := range expectedProtocols {
|
||||
if expectedProtocol == protocol {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("protocol should be in %v and is %v.", expectedProtocols, protocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
-527
@@ -1,527 +0,0 @@
|
||||
package curl
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <curl/curl.h>
|
||||
#include "callback.h"
|
||||
#include "compat.h"
|
||||
|
||||
static CURLcode curl_easy_setopt_long(CURL *handle, CURLoption option, long parameter) {
|
||||
return curl_easy_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLcode curl_easy_setopt_string(CURL *handle, CURLoption option, char *parameter) {
|
||||
return curl_easy_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLcode curl_easy_setopt_slist(CURL *handle, CURLoption option, struct curl_slist *parameter) {
|
||||
return curl_easy_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLcode curl_easy_setopt_pointer(CURL *handle, CURLoption option, void *parameter) {
|
||||
return curl_easy_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLcode curl_easy_setopt_off_t(CURL *handle, CURLoption option, off_t parameter) {
|
||||
return curl_easy_setopt(handle, option, parameter);
|
||||
}
|
||||
|
||||
static CURLcode curl_easy_getinfo_string(CURL *curl, CURLINFO info, char **p) {
|
||||
return curl_easy_getinfo(curl, info, p);
|
||||
}
|
||||
static CURLcode curl_easy_getinfo_long(CURL *curl, CURLINFO info, long *p) {
|
||||
return curl_easy_getinfo(curl, info, p);
|
||||
}
|
||||
static CURLcode curl_easy_getinfo_double(CURL *curl, CURLINFO info, double *p) {
|
||||
return curl_easy_getinfo(curl, info, p);
|
||||
}
|
||||
static CURLcode curl_easy_getinfo_slist(CURL *curl, CURLINFO info, struct curl_slist **p) {
|
||||
return curl_easy_getinfo(curl, info, p);
|
||||
}
|
||||
|
||||
static CURLFORMcode curl_formadd_name_content_length(
|
||||
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length) {
|
||||
return curl_formadd(httppost, last_post,
|
||||
CURLFORM_COPYNAME, name,
|
||||
CURLFORM_COPYCONTENTS, content,
|
||||
CURLFORM_CONTENTSLENGTH, length, CURLFORM_END);
|
||||
}
|
||||
static CURLFORMcode curl_formadd_name_content_length_type(
|
||||
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length, char *type) {
|
||||
return curl_formadd(httppost, last_post,
|
||||
CURLFORM_COPYNAME, name,
|
||||
CURLFORM_COPYCONTENTS, content,
|
||||
CURLFORM_CONTENTSLENGTH, length,
|
||||
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
|
||||
}
|
||||
static CURLFORMcode curl_formadd_name_file_type(
|
||||
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *filename, char *type) {
|
||||
return curl_formadd(httppost, last_post,
|
||||
CURLFORM_COPYNAME, name,
|
||||
CURLFORM_FILE, filename,
|
||||
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
|
||||
}
|
||||
// TODO: support multi file
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"path"
|
||||
"unsafe"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CurlInfo C.CURLINFO
|
||||
type CurlError C.CURLcode
|
||||
|
||||
type CurlString *C.char
|
||||
|
||||
func NewCurlString(s string) CurlString {
|
||||
return CurlString(unsafe.Pointer(C.CString(s)))
|
||||
}
|
||||
|
||||
func FreeCurlString(s CurlString) {
|
||||
C.free(unsafe.Pointer(s))
|
||||
}
|
||||
|
||||
func (e CurlError) Error() string {
|
||||
// ret is const char*, no need to free
|
||||
ret := C.curl_easy_strerror(C.CURLcode(e))
|
||||
return fmt.Sprintf("curl: %s", C.GoString(ret))
|
||||
}
|
||||
|
||||
func newCurlError(errno C.CURLcode) error {
|
||||
if errno == C.CURLE_OK { // if nothing wrong
|
||||
return nil
|
||||
}
|
||||
return CurlError(errno)
|
||||
}
|
||||
|
||||
// curl_easy interface
|
||||
type CURL struct {
|
||||
handle unsafe.Pointer
|
||||
// callback functions, bool ret means ok or not
|
||||
headerFunction, writeFunction *func([]byte, interface{}) bool
|
||||
readFunction *func([]byte, interface{}) int // return num of bytes writed to buf
|
||||
progressFunction *func(float64, float64, float64, float64, interface{}) bool
|
||||
fnmatchFunction *func(string, string, interface{}) int
|
||||
// callback datas
|
||||
headerData, writeData, readData, progressData, fnmatchData interface{}
|
||||
// list of C allocs
|
||||
mallocAllocs []*C.char
|
||||
}
|
||||
|
||||
// concurrent safe context map
|
||||
type contextMap struct {
|
||||
items map[uintptr]*CURL
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *contextMap) Set(k uintptr, v *CURL) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
c.items[k] = v
|
||||
}
|
||||
|
||||
func (c *contextMap) Get(k uintptr) *CURL {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c.items[k]
|
||||
}
|
||||
|
||||
func (c *contextMap) Delete(k uintptr) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
delete(c.items, k)
|
||||
}
|
||||
|
||||
var context_map = &contextMap {
|
||||
items: make(map[uintptr]*CURL),
|
||||
}
|
||||
|
||||
// curl_easy_init - Start a libcurl easy session
|
||||
func EasyInit() *CURL {
|
||||
p := C.curl_easy_init()
|
||||
c := &CURL{handle: p, mallocAllocs: make([]*C.char, 0)} // other field defaults to nil
|
||||
context_map.Set(uintptr(p), c)
|
||||
return c
|
||||
}
|
||||
|
||||
// curl_easy_duphandle - Clone a libcurl session handle
|
||||
func (curl *CURL) Duphandle() *CURL {
|
||||
p := C.curl_easy_duphandle(curl.handle)
|
||||
c := &CURL{handle: p}
|
||||
context_map.Set(uintptr(p), c)
|
||||
return c
|
||||
}
|
||||
|
||||
// curl_easy_cleanup - End a libcurl easy session
|
||||
func (curl *CURL) Cleanup() {
|
||||
p := curl.handle
|
||||
C.curl_easy_cleanup(p)
|
||||
curl.MallocFreeAfter(0)
|
||||
context_map.Delete(uintptr(p))
|
||||
}
|
||||
|
||||
// curl_easy_setopt - set options for a curl easy handle
|
||||
// WARNING: a function pointer is &fun, but function addr is reflect.ValueOf(fun).Pointer()
|
||||
func (curl *CURL) Setopt(opt int, param interface{}) error {
|
||||
p := curl.handle
|
||||
if param == nil {
|
||||
// NOTE: some option will crash program when got a nil param
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), nil))
|
||||
}
|
||||
switch {
|
||||
// not really set
|
||||
case opt == OPT_READDATA: // OPT_INFILE
|
||||
curl.readData = param
|
||||
return nil
|
||||
case opt == OPT_PROGRESSDATA:
|
||||
curl.progressData = param
|
||||
return nil
|
||||
case opt == OPT_HEADERDATA: // also known as OPT_WRITEHEADER
|
||||
curl.headerData = param
|
||||
return nil
|
||||
case opt == OPT_WRITEDATA: // OPT_FILE
|
||||
curl.writeData = param
|
||||
return nil
|
||||
|
||||
case opt == OPT_READFUNCTION:
|
||||
fun := param.(func([]byte, interface{}) int)
|
||||
curl.readFunction = &fun
|
||||
|
||||
ptr := C.return_read_function()
|
||||
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_READDATA, unsafe.Pointer(curl.handle)))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case opt == OPT_PROGRESSFUNCTION:
|
||||
fun := param.(func(float64, float64, float64, float64, interface{}) bool)
|
||||
curl.progressFunction = &fun
|
||||
|
||||
ptr := C.return_progress_function()
|
||||
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_PROGRESSDATA, unsafe.Pointer(curl.handle)))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case opt == OPT_HEADERFUNCTION:
|
||||
fun := param.(func([]byte, interface{}) bool)
|
||||
curl.headerFunction = &fun
|
||||
|
||||
ptr := C.return_header_function()
|
||||
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_HEADERDATA, unsafe.Pointer(curl.handle)))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case opt == OPT_WRITEFUNCTION:
|
||||
fun := param.(func([]byte, interface{}) bool)
|
||||
curl.writeFunction = &fun
|
||||
|
||||
ptr := C.return_write_function()
|
||||
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_WRITEDATA, unsafe.Pointer(curl.handle)))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
// for OPT_HTTPPOST, use struct Form
|
||||
case opt == OPT_HTTPPOST:
|
||||
post := param.(*Form)
|
||||
ptr := post.head
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), unsafe.Pointer(ptr)))
|
||||
|
||||
case opt >= C.CURLOPTTYPE_OFF_T:
|
||||
val := C.off_t(0)
|
||||
switch t := param.(type) {
|
||||
case int:
|
||||
val = C.off_t(t)
|
||||
case uint64:
|
||||
val = C.off_t(t)
|
||||
default:
|
||||
panic("OFF_T conversion not supported")
|
||||
}
|
||||
return newCurlError(C.curl_easy_setopt_off_t(p, C.CURLoption(opt), val))
|
||||
|
||||
case opt >= C.CURLOPTTYPE_FUNCTIONPOINT:
|
||||
// function pointer
|
||||
panic("function pointer not implemented yet!")
|
||||
|
||||
case opt >= C.CURLOPTTYPE_OBJECTPOINT:
|
||||
switch t := param.(type) {
|
||||
case string:
|
||||
ptr := C.CString(t)
|
||||
curl.mallocAddPtr(ptr)
|
||||
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
|
||||
case CurlString:
|
||||
ptr := (*C.char)(t)
|
||||
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
|
||||
case []string:
|
||||
if len(t) > 0 {
|
||||
ptr := C.CString(t[0])
|
||||
curl.mallocAddPtr(ptr)
|
||||
a_slist := C.curl_slist_append(nil, ptr)
|
||||
for _, s := range t[1:] {
|
||||
ptr := C.CString(s)
|
||||
curl.mallocAddPtr(ptr)
|
||||
a_slist = C.curl_slist_append(a_slist, ptr)
|
||||
}
|
||||
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
|
||||
} else {
|
||||
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
|
||||
}
|
||||
case []CurlString:
|
||||
if len(t) > 0 {
|
||||
ptr := (*C.char)(t[0])
|
||||
a_slist := C.curl_slist_append(nil, ptr)
|
||||
for _, s := range t[1:] {
|
||||
ptr := (*C.char)(s)
|
||||
a_slist = C.curl_slist_append(a_slist, ptr)
|
||||
}
|
||||
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
|
||||
} else {
|
||||
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
|
||||
}
|
||||
default:
|
||||
// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
|
||||
// val := reflect.ValueOf(param)
|
||||
//fmt.Printf("DEBUG(Setopt): param=%x\n", val.Pointer())
|
||||
//println("DEBUG can addr =", val.Pointer(), "opt=", opt)
|
||||
// pass a pointer to GoInterface
|
||||
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt),
|
||||
unsafe.Pointer(¶m)))
|
||||
}
|
||||
case opt >= C.CURLOPTTYPE_LONG:
|
||||
val := C.long(0)
|
||||
switch t := param.(type) {
|
||||
case int:
|
||||
val = C.long(t)
|
||||
case bool:
|
||||
if t {
|
||||
val = 1
|
||||
}
|
||||
case int64:
|
||||
val = C.long(t)
|
||||
case int32:
|
||||
val = C.long(t)
|
||||
default:
|
||||
panic("not supported converstion to c long")
|
||||
}
|
||||
return newCurlError(C.curl_easy_setopt_long(p, C.CURLoption(opt), val))
|
||||
}
|
||||
panic("opt param error!")
|
||||
}
|
||||
|
||||
// curl_easy_send - sends raw data over an "easy" connection
|
||||
func (curl *CURL) Send(buffer []byte) (int, error) {
|
||||
p := curl.handle
|
||||
buflen := len(buffer)
|
||||
n := C.size_t(0)
|
||||
ret := C.curl_easy_send(p, unsafe.Pointer(&buffer[0]), C.size_t(buflen), &n)
|
||||
return int(n), newCurlError(ret)
|
||||
}
|
||||
|
||||
// curl_easy_recv - receives raw data on an "easy" connection
|
||||
func (curl *CURL) Recv(buffer []byte) (int, error) {
|
||||
p := curl.handle
|
||||
buflen := len(buffer)
|
||||
buf := C.CString(string(buffer))
|
||||
n := C.size_t(0)
|
||||
ret := C.curl_easy_recv(p, unsafe.Pointer(buf), C.size_t(buflen), &n)
|
||||
return copy(buffer, C.GoStringN(buf, C.int(n))), newCurlError(ret)
|
||||
}
|
||||
|
||||
// curl_easy_perform - Perform a file transfer
|
||||
func (curl *CURL) Perform() error {
|
||||
p := curl.handle
|
||||
return newCurlError(C.curl_easy_perform(p))
|
||||
}
|
||||
|
||||
// curl_easy_pause - pause and unpause a connection
|
||||
func (curl *CURL) Pause(bitmask int) error {
|
||||
p := curl.handle
|
||||
return newCurlError(C.curl_easy_pause(p, C.int(bitmask)))
|
||||
}
|
||||
|
||||
// curl_easy_reset - reset all options of a libcurl session handle
|
||||
func (curl *CURL) Reset() {
|
||||
p := curl.handle
|
||||
C.curl_easy_reset(p)
|
||||
}
|
||||
|
||||
// curl_easy_escape - URL encodes the given string
|
||||
func (curl *CURL) Escape(url string) string {
|
||||
p := curl.handle
|
||||
oldUrl := C.CString(url)
|
||||
defer C.free(unsafe.Pointer(oldUrl))
|
||||
newUrl := C.curl_easy_escape(p, oldUrl, 0)
|
||||
defer C.curl_free(unsafe.Pointer(newUrl))
|
||||
return C.GoString(newUrl)
|
||||
}
|
||||
|
||||
// curl_easy_unescape - URL decodes the given string
|
||||
func (curl *CURL) Unescape(url string) string {
|
||||
p := curl.handle
|
||||
oldUrl := C.CString(url)
|
||||
outlength := C.int(0)
|
||||
defer C.free(unsafe.Pointer(oldUrl))
|
||||
// If outlength is non-NULL, the function will write the length of the
|
||||
// returned string in the integer it points to. This allows an
|
||||
// escaped string containing %00 to still get used properly after unescaping.
|
||||
newUrl := C.curl_easy_unescape(p, oldUrl, 0, &outlength)
|
||||
defer C.curl_free(unsafe.Pointer(newUrl))
|
||||
return C.GoStringN(newUrl, outlength)
|
||||
}
|
||||
|
||||
// curl_easy_getinfo - extract information from a curl handle
|
||||
func (curl *CURL) Getinfo(info CurlInfo) (ret interface{}, err error) {
|
||||
p := curl.handle
|
||||
cInfo := C.CURLINFO(info)
|
||||
switch cInfo & C.CURLINFO_TYPEMASK {
|
||||
case C.CURLINFO_STRING:
|
||||
a_string := C.CString("")
|
||||
defer C.free(unsafe.Pointer(a_string))
|
||||
err := newCurlError(C.curl_easy_getinfo_string(p, cInfo, &a_string))
|
||||
ret := C.GoString(a_string)
|
||||
debugf("Getinfo %s", ret)
|
||||
return ret, err
|
||||
case C.CURLINFO_LONG:
|
||||
a_long := C.long(-1)
|
||||
err := newCurlError(C.curl_easy_getinfo_long(p, cInfo, &a_long))
|
||||
ret := int(a_long)
|
||||
debugf("Getinfo %s", ret)
|
||||
return ret, err
|
||||
case C.CURLINFO_DOUBLE:
|
||||
a_double := C.double(0.0)
|
||||
err := newCurlError(C.curl_easy_getinfo_double(p, cInfo, &a_double))
|
||||
ret := float64(a_double)
|
||||
debugf("Getinfo %s", ret)
|
||||
return ret, err
|
||||
case C.CURLINFO_SLIST:
|
||||
a_ptr_slist := new(_Ctype_struct_curl_slist)
|
||||
err := newCurlError(C.curl_easy_getinfo_slist(p, cInfo, &a_ptr_slist))
|
||||
ret := []string{}
|
||||
for a_ptr_slist != nil {
|
||||
debugf("Getinfo %s %v", C.GoString(a_ptr_slist.data), a_ptr_slist.next)
|
||||
ret = append(ret, C.GoString(a_ptr_slist.data))
|
||||
a_ptr_slist = a_ptr_slist.next
|
||||
}
|
||||
return ret, err
|
||||
default:
|
||||
panic("error calling Getinfo\n")
|
||||
}
|
||||
panic("not implemented yet!")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (curl *CURL) GetHandle() unsafe.Pointer {
|
||||
return curl.handle
|
||||
}
|
||||
|
||||
func (curl *CURL) MallocGetPos() int {
|
||||
return len(curl.mallocAllocs)
|
||||
}
|
||||
|
||||
func (curl *CURL) MallocFreeAfter(from int) {
|
||||
l := len(curl.mallocAllocs)
|
||||
for idx := from; idx < l; idx++ {
|
||||
C.free(unsafe.Pointer(curl.mallocAllocs[idx]))
|
||||
curl.mallocAllocs[idx] = nil
|
||||
}
|
||||
curl.mallocAllocs = curl.mallocAllocs[0:from]
|
||||
}
|
||||
|
||||
func (curl *CURL) mallocAddPtr(ptr *C.char) {
|
||||
curl.mallocAllocs = append(curl.mallocAllocs, ptr)
|
||||
}
|
||||
|
||||
// A multipart/formdata HTTP POST form
|
||||
type Form struct {
|
||||
head, last *C.struct_curl_httppost
|
||||
}
|
||||
|
||||
func NewForm() *Form {
|
||||
return &Form{}
|
||||
}
|
||||
|
||||
func (form *Form) Add(name string, content interface{}) error {
|
||||
head, last := form.head, form.last
|
||||
namestr := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(namestr))
|
||||
var (
|
||||
buffer *C.char
|
||||
length C.int
|
||||
)
|
||||
switch t := content.(type) {
|
||||
case string:
|
||||
buffer = C.CString(t)
|
||||
length = C.int(len(t))
|
||||
case []byte:
|
||||
buffer = C.CString(string(t))
|
||||
length = C.int(len(t))
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
defer C.free(unsafe.Pointer(buffer))
|
||||
C.curl_formadd_name_content_length(&head, &last, namestr, buffer, length)
|
||||
form.head, form.last = head, last
|
||||
return nil
|
||||
}
|
||||
|
||||
func (form *Form) AddWithType(name string, content interface{}, content_type string) error {
|
||||
head, last := form.head, form.last
|
||||
namestr := C.CString(name)
|
||||
typestr := C.CString(content_type)
|
||||
defer C.free(unsafe.Pointer(namestr))
|
||||
defer C.free(unsafe.Pointer(typestr))
|
||||
var (
|
||||
buffer *C.char
|
||||
length C.int
|
||||
)
|
||||
switch t := content.(type) {
|
||||
case string:
|
||||
buffer = C.CString(t)
|
||||
length = C.int(len(t))
|
||||
case []byte:
|
||||
buffer = C.CString(string(t))
|
||||
length = C.int(len(t))
|
||||
default:
|
||||
panic("not implemented")
|
||||
}
|
||||
defer C.free(unsafe.Pointer(buffer))
|
||||
C.curl_formadd_name_content_length_type(&head, &last, namestr, buffer, length, typestr)
|
||||
form.head, form.last = head, last
|
||||
return nil
|
||||
}
|
||||
|
||||
func (form *Form) AddFile(name, filename string) error {
|
||||
head, last := form.head, form.last
|
||||
namestr := C.CString(name)
|
||||
pathstr := C.CString(filename)
|
||||
typestr := C.CString(guessType(filename))
|
||||
defer C.free(unsafe.Pointer(namestr))
|
||||
defer C.free(unsafe.Pointer(pathstr))
|
||||
defer C.free(unsafe.Pointer(typestr))
|
||||
C.curl_formadd_name_file_type(&head, &last, namestr, pathstr, typestr)
|
||||
form.head, form.last = head, last
|
||||
return nil
|
||||
}
|
||||
|
||||
func (form *Form) AddFromFile(name, filename string) {
|
||||
}
|
||||
|
||||
func guessType(filename string) string {
|
||||
ext := path.Ext(filename)
|
||||
file_type := mime.TypeByExtension(ext)
|
||||
if file_type == "" {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
return file_type
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
package curl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func setupTestServer(serverContent string) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, serverContent)
|
||||
}))
|
||||
}
|
||||
|
||||
func TestEasyInterface(t *testing.T) {
|
||||
ts := setupTestServer("")
|
||||
defer ts.Close()
|
||||
|
||||
easy := EasyInit()
|
||||
defer easy.Cleanup()
|
||||
|
||||
easy.Setopt(OPT_URL, ts.URL)
|
||||
if err := easy.Perform(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallbackFunction(t *testing.T) {
|
||||
serverContent := "A random string"
|
||||
ts := setupTestServer(serverContent)
|
||||
defer ts.Close()
|
||||
|
||||
easy := EasyInit()
|
||||
defer easy.Cleanup()
|
||||
|
||||
easy.Setopt(OPT_URL, ts.URL)
|
||||
easy.Setopt(OPT_WRITEFUNCTION, func(buf []byte, userdata interface{}) bool {
|
||||
result := string(buf)
|
||||
expected := serverContent + "\n"
|
||||
if result != expected {
|
||||
t.Errorf("output should be %q and is %q.", expected, result)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if err := easy.Perform(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscape(t *testing.T) {
|
||||
easy := EasyInit()
|
||||
defer easy.Cleanup()
|
||||
|
||||
payload := `payload={"msg": "First line\nSecond Line"}`
|
||||
expected := `payload%3D%7B%22msg%22%3A%20%22First%20line%5CnSecond%20Line%22%7D`
|
||||
result := easy.Escape(payload)
|
||||
if result != expected {
|
||||
t.Errorf("escaped output should be %q and is %q.", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentInitAndCleanup(t *testing.T) {
|
||||
c := 2
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(c)
|
||||
for i := 0; i < c; i++ {
|
||||
go func() {
|
||||
wg.Done()
|
||||
easy := EasyInit()
|
||||
defer easy.Cleanup()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package curl
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
_DEBUG = 10 * (iota + 1)
|
||||
_INFO
|
||||
_WARN
|
||||
_ERROR
|
||||
)
|
||||
|
||||
const _DEFAULT_LOG_LEVEL = _WARN
|
||||
|
||||
var log_level = _DEFAULT_LOG_LEVEL
|
||||
|
||||
// SetLogLevel changes the log level which determines the granularity of the
|
||||
// messages that are logged. Available log levels are: "DEBUG", "INFO",
|
||||
// "WARN", "ERROR" and "DEFAULT_LOG_LEVEL".
|
||||
func SetLogLevel(levelName string) {
|
||||
switch levelName {
|
||||
case "DEBUG":
|
||||
log_level = _DEBUG
|
||||
case "INFO":
|
||||
log_level = _INFO
|
||||
case "WARN":
|
||||
log_level = _WARN
|
||||
case "ERROR":
|
||||
log_level = _ERROR
|
||||
case "DEFAULT_LOG_LEVEL":
|
||||
log_level = _DEFAULT_LOG_LEVEL
|
||||
}
|
||||
}
|
||||
|
||||
func logf(limitLevel int, format string, args ...interface{}) {
|
||||
if log_level <= limitLevel {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func debugf(format string, args ...interface{}) {
|
||||
logf(_DEBUG, format, args...)
|
||||
}
|
||||
|
||||
func infof(format string, args ...interface{}) {
|
||||
logf(_INFO, format, args...)
|
||||
}
|
||||
|
||||
func warnf(format string, args ...interface{}) {
|
||||
logf(_WARN, format, args...)
|
||||
}
|
||||
|
||||
func errorf(format string, args ...interface{}) {
|
||||
logf(_ERROR, format, args...)
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
|
||||
package curl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func TestDefaultLogLevel(t *testing.T) {
|
||||
if log_level != _DEFAULT_LOG_LEVEL {t.Error("Test failed, expected DEFAULT_LOG_LEVEL level.")}
|
||||
}
|
||||
|
||||
func TestSetLogLevel(t *testing.T) {
|
||||
SetLogLevel("DEBUG")
|
||||
defer SetLogLevel("DEFAULT_LOG_LEVEL")
|
||||
if log_level != _DEBUG {t.Error("Test failed, expected DEBUG level.")}
|
||||
SetLogLevel("INFO")
|
||||
if log_level != _INFO {t.Error("Test failed, expected INFO level.")}
|
||||
SetLogLevel("WARN")
|
||||
if log_level != _WARN {t.Error("Test failed, expected WARN level.")}
|
||||
SetLogLevel("ERROR")
|
||||
if log_level != _ERROR {t.Error("Test failed, expected ERROR level.")}
|
||||
}
|
||||
|
||||
var (
|
||||
testFormat = "test format %s"
|
||||
testArgument = "test string 1"
|
||||
expectedRegexp = regexp.MustCompile(".*" + fmt.Sprintf(testFormat, testArgument) + "\n$")
|
||||
)
|
||||
|
||||
|
||||
func TestLogf(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
log.SetOutput(buf)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
SetLogLevel("DEBUG")
|
||||
defer SetLogLevel("DEFAULT_LOG_LEVEL")
|
||||
|
||||
logf(_DEBUG, testFormat, testArgument)
|
||||
line := buf.String()
|
||||
matched := expectedRegexp.MatchString(line)
|
||||
if !matched {
|
||||
t.Errorf("log output should match %q and is %q.", expectedRegexp, line)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogfUsesLogLevel(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
log.SetOutput(buf)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
SetLogLevel("WARN")
|
||||
defer SetLogLevel("DEFAULT_LOG_LEVEL")
|
||||
|
||||
logf(_DEBUG, testFormat, testArgument)
|
||||
line := buf.String()
|
||||
expectedLine := ""
|
||||
if line != expectedLine {
|
||||
t.Errorf("log output should match %q and is %q.", expectedLine, line)
|
||||
}
|
||||
}
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
// This file depends on functionality not available on Windows, hence we
|
||||
// must skip it. https://github.com/andelf/go-curl/issues/48
|
||||
|
||||
// +build !windows
|
||||
|
||||
package curl
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
static CURLMcode curl_multi_setopt_long(CURLM *handle, CURLMoption option, long parameter) {
|
||||
return curl_multi_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLMcode curl_multi_setopt_pointer(CURLM *handle, CURLMoption option, void *parameter) {
|
||||
return curl_multi_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLMcode curl_multi_fdset_pointer(CURLM *handle,
|
||||
void *read_fd_set,
|
||||
void *write_fd_set,
|
||||
void *exc_fd_set,
|
||||
int *max_fd)
|
||||
{
|
||||
return curl_multi_fdset(handle, read_fd_set, write_fd_set, exc_fd_set, max_fd);
|
||||
}
|
||||
static CURLMsg *curl_multi_info_read_pointer(CURLM *handle, int *msgs_in_queue)
|
||||
{
|
||||
return curl_multi_info_read(handle, msgs_in_queue);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type CurlMultiError C.CURLMcode
|
||||
type CurlMultiMsg C.CURLMSG
|
||||
|
||||
func (e CurlMultiError) Error() string {
|
||||
// ret is const char*, no need to free
|
||||
ret := C.curl_multi_strerror(C.CURLMcode(e))
|
||||
return C.GoString(ret)
|
||||
}
|
||||
|
||||
func newCurlMultiError(errno C.CURLMcode) error {
|
||||
// cannot use C.CURLM_OK here, cause multi.h use a undefined emum num
|
||||
if errno == 0 { // if nothing wrong
|
||||
return nil
|
||||
}
|
||||
return CurlMultiError(errno)
|
||||
}
|
||||
|
||||
func newCURLMessage(message *C.CURLMsg) (msg *CURLMessage){
|
||||
if message == nil {
|
||||
return nil
|
||||
}
|
||||
msg = new(CURLMessage)
|
||||
msg.Msg = CurlMultiMsg(message.msg)
|
||||
msg.Easy_handle = &CURL{handle: message.easy_handle}
|
||||
msg.Data = message.data
|
||||
return msg
|
||||
}
|
||||
|
||||
type CURLM struct {
|
||||
handle unsafe.Pointer
|
||||
}
|
||||
|
||||
var dummy unsafe.Pointer
|
||||
type CURLMessage struct {
|
||||
Msg CurlMultiMsg
|
||||
Easy_handle *CURL
|
||||
Data [unsafe.Sizeof(dummy)]byte
|
||||
}
|
||||
|
||||
// curl_multi_init - create a multi handle
|
||||
func MultiInit() *CURLM {
|
||||
p := C.curl_multi_init()
|
||||
return &CURLM{p}
|
||||
}
|
||||
|
||||
// curl_multi_cleanup - close down a multi session
|
||||
func (mcurl *CURLM) Cleanup() error {
|
||||
p := mcurl.handle
|
||||
return newCurlMultiError(C.curl_multi_cleanup(p))
|
||||
}
|
||||
|
||||
// curl_multi_perform - reads/writes available data from each easy handle
|
||||
func (mcurl *CURLM) Perform() (int, error) {
|
||||
p := mcurl.handle
|
||||
running_handles := C.int(-1)
|
||||
err := newCurlMultiError(C.curl_multi_perform(p, &running_handles))
|
||||
return int(running_handles), err
|
||||
}
|
||||
|
||||
// curl_multi_add_handle - add an easy handle to a multi session
|
||||
func (mcurl *CURLM) AddHandle(easy *CURL) error {
|
||||
mp := mcurl.handle
|
||||
easy_handle := easy.handle
|
||||
return newCurlMultiError(C.curl_multi_add_handle(mp, easy_handle))
|
||||
}
|
||||
|
||||
// curl_multi_remove_handle - remove an easy handle from a multi session
|
||||
func (mcurl *CURLM) RemoveHandle(easy *CURL) error {
|
||||
mp := mcurl.handle
|
||||
easy_handle := easy.handle
|
||||
return newCurlMultiError(C.curl_multi_remove_handle(mp, easy_handle))
|
||||
}
|
||||
|
||||
func (mcurl *CURLM) Timeout() (int, error) {
|
||||
p := mcurl.handle
|
||||
timeout := C.long(-1)
|
||||
err := newCurlMultiError(C.curl_multi_timeout(p, &timeout))
|
||||
return int(timeout), err
|
||||
}
|
||||
|
||||
func (mcurl *CURLM) Setopt(opt int, param interface{}) error {
|
||||
p := mcurl.handle
|
||||
if param == nil {
|
||||
return newCurlMultiError(C.curl_multi_setopt_pointer(p, C.CURLMoption(opt), nil))
|
||||
}
|
||||
switch {
|
||||
// currently cannot support these option
|
||||
// case MOPT_SOCKETFUNCTION, MOPT_SOCKETDATA, MOPT_TIMERFUNCTION, MOPT_TIMERDATA:
|
||||
// panic("not supported CURLM.Setopt opt")
|
||||
case opt >= C.CURLOPTTYPE_LONG:
|
||||
val := C.long(0)
|
||||
switch t := param.(type) {
|
||||
case int:
|
||||
val := C.long(t)
|
||||
return newCurlMultiError(C.curl_multi_setopt_long(p, C.CURLMoption(opt), val))
|
||||
case bool:
|
||||
val = C.long(0)
|
||||
if t {
|
||||
val = C.long(1)
|
||||
}
|
||||
return newCurlMultiError(C.curl_multi_setopt_long(p, C.CURLMoption(opt), val))
|
||||
}
|
||||
}
|
||||
panic("not supported CURLM.Setopt opt or param")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mcurl *CURLM) Fdset(rset, wset, eset *syscall.FdSet) (int, error) {
|
||||
p := mcurl.handle
|
||||
read := unsafe.Pointer(rset)
|
||||
write := unsafe.Pointer(wset)
|
||||
exc := unsafe.Pointer(eset)
|
||||
maxfd := C.int(-1)
|
||||
err := newCurlMultiError(C.curl_multi_fdset_pointer(p, read, write,
|
||||
exc, &maxfd))
|
||||
return int(maxfd), err
|
||||
}
|
||||
|
||||
func (mcurl *CURLM) Info_read() (*CURLMessage, int) {
|
||||
p := mcurl.handle
|
||||
left := C.int(0)
|
||||
return newCURLMessage(C.curl_multi_info_read_pointer(p, &left)), int(left)
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
package curl
|
||||
|
||||
/*
|
||||
#include <curl/curl.h>
|
||||
|
||||
static CURLSHcode curl_share_setopt_long(CURLSH *handle, CURLSHoption option, long parameter) {
|
||||
return curl_share_setopt(handle, option, parameter);
|
||||
}
|
||||
static CURLSHcode curl_share_setopt_pointer(CURLSH *handle, CURLSHoption option, void *parameter) {
|
||||
return curl_share_setopt(handle, option, parameter);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// implement os.Error interface
|
||||
type CurlShareError C.CURLMcode
|
||||
|
||||
func (e CurlShareError) Error() string {
|
||||
// ret is const char*, no need to free
|
||||
ret := C.curl_share_strerror(C.CURLSHcode(e))
|
||||
return C.GoString(ret)
|
||||
}
|
||||
|
||||
func newCurlShareError(errno C.CURLSHcode) error {
|
||||
if errno == 0 { // if nothing wrong
|
||||
return nil
|
||||
}
|
||||
return CurlShareError(errno)
|
||||
}
|
||||
|
||||
type CURLSH struct {
|
||||
handle unsafe.Pointer
|
||||
}
|
||||
|
||||
func ShareInit() *CURLSH {
|
||||
p := C.curl_share_init()
|
||||
return &CURLSH{p}
|
||||
}
|
||||
|
||||
func (shcurl *CURLSH) Cleanup() error {
|
||||
p := shcurl.handle
|
||||
return newCurlShareError(C.curl_share_cleanup(p))
|
||||
}
|
||||
|
||||
func (shcurl *CURLSH) Setopt(opt int, param interface{}) error {
|
||||
p := shcurl.handle
|
||||
if param == nil {
|
||||
return newCurlShareError(C.curl_share_setopt_pointer(p, C.CURLSHoption(opt), nil))
|
||||
}
|
||||
switch opt {
|
||||
// case SHOPT_LOCKFUNC, SHOPT_UNLOCKFUNC, SHOPT_USERDATA:
|
||||
// panic("not supported")
|
||||
case SHOPT_SHARE, SHOPT_UNSHARE:
|
||||
if val, ok := param.(int); ok {
|
||||
return newCurlShareError(C.curl_share_setopt_long(p, C.CURLSHoption(opt), C.long(val)))
|
||||
}
|
||||
}
|
||||
panic("not supported CURLSH.Setopt opt or param")
|
||||
return nil
|
||||
}
|
||||
-22
@@ -1,22 +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
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
language: go
|
||||
go_import_path: github.com/davecgh/go-spew
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- tip
|
||||
sudo: false
|
||||
install:
|
||||
- go get -v github.com/alecthomas/gometalinter
|
||||
- gometalinter --install
|
||||
script:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- export GORACE="halt_on_error=1"
|
||||
- test -z "$(gometalinter --disable-all
|
||||
--enable=gofmt
|
||||
--enable=golint
|
||||
--enable=vet
|
||||
--enable=gosimple
|
||||
--enable=unconvert
|
||||
--deadline=4m ./spew | tee /dev/stderr)"
|
||||
- go test -v -race -tags safe ./spew
|
||||
- go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov
|
||||
after_success:
|
||||
- go get -v github.com/mattn/goveralls
|
||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
go-spew
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/davecgh/go-spew)
|
||||
[](http://copyfree.org)
|
||||
[](https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||
|
||||
Go-spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
||||
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
|
||||
report. Go-spew is licensed under the liberal ISC license, so it may be used in
|
||||
open source or commercial projects.
|
||||
|
||||
If you're interested in reading about how this package came to life and some
|
||||
of the challenges involved in providing a deep pretty printer, there is a blog
|
||||
post about it
|
||||
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
|
||||
|
||||
## Documentation
|
||||
|
||||
[](http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||
|
||||
Full `go doc` style documentation for the project can be viewed online without
|
||||
installing this package by using the excellent GoDoc site here:
|
||||
http://godoc.org/github.com/davecgh/go-spew/spew
|
||||
|
||||
You can also view the documentation locally once the package is installed with
|
||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
||||
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/davecgh/go-spew/spew
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Add this import line to the file you're working in:
|
||||
|
||||
```Go
|
||||
import "github.com/davecgh/go-spew/spew"
|
||||
```
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
|
||||
```Go
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
```
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
|
||||
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
|
||||
and pointer addresses):
|
||||
|
||||
```Go
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
```
|
||||
|
||||
## Debugging a Web Application Example
|
||||
|
||||
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
|
||||
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Dump Output
|
||||
|
||||
```
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) {
|
||||
(string) "one": (bool) true
|
||||
}
|
||||
}
|
||||
([]uint8) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
```
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
```
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
```
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available via the
|
||||
spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
```
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables. This option
|
||||
relies on access to the unsafe package, so it will not have any effect when
|
||||
running in environments without access to the unsafe package such as Google
|
||||
App Engine or with the "safe" build tag specified.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of capacities
|
||||
for arrays, slices, maps and channels. This is useful when diffing data
|
||||
structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are supported,
|
||||
with other types sorted according to the reflect.Value.String() output
|
||||
which guarantees display stability. Natural map order is used by
|
||||
default.
|
||||
|
||||
* SpewKeys
|
||||
SpewKeys specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only considered
|
||||
if SortKeys is true.
|
||||
|
||||
```
|
||||
|
||||
## Unsafe Package Dependency
|
||||
|
||||
This package relies on the unsafe package to perform some of the more advanced
|
||||
features, however it also supports a "limited" mode which allows it to work in
|
||||
environments where the unsafe package is not available. By default, it will
|
||||
operate in this mode on Google App Engine and when compiled with GopherJS. The
|
||||
"safe" build tag may also be specified to force the package to build without
|
||||
using the unsafe package.
|
||||
|
||||
## License
|
||||
|
||||
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script uses gocov to generate a test coverage report.
|
||||
# The gocov tool my be obtained with the following command:
|
||||
# go get github.com/axw/gocov/gocov
|
||||
#
|
||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
||||
|
||||
# Check for gocov.
|
||||
if ! type gocov >/dev/null 2>&1; then
|
||||
echo >&2 "This script requires the gocov tool."
|
||||
echo >&2 "You may obtain it with the following command:"
|
||||
echo >&2 "go get github.com/axw/gocov/gocov"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Only run the cgo tests if gcc is installed.
|
||||
if type gcc >/dev/null 2>&1; then
|
||||
(cd spew && gocov test -tags testcgo | gocov report)
|
||||
else
|
||||
(cd spew && gocov test | gocov report)
|
||||
fi
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = false
|
||||
|
||||
// ptrSize is the size of a pointer on the current arch.
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
type flag uintptr
|
||||
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
// the typical safety restrictions preventing access to unaddressable and
|
||||
// unexported data. It works by digging the raw pointer to the underlying
|
||||
// value out of the protected value and generating a new unprotected (unsafe)
|
||||
// reflect.Value to it.
|
||||
//
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
||||
-341
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||
// the technique used in the fmt package.
|
||||
var (
|
||||
panicBytes = []byte("(PANIC=")
|
||||
plusBytes = []byte("+")
|
||||
iBytes = []byte("i")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
interfaceBytes = []byte("(interface {})")
|
||||
commaNewlineBytes = []byte(",\n")
|
||||
newlineBytes = []byte("\n")
|
||||
openBraceBytes = []byte("{")
|
||||
openBraceNewlineBytes = []byte("{\n")
|
||||
closeBraceBytes = []byte("}")
|
||||
asteriskBytes = []byte("*")
|
||||
colonBytes = []byte(":")
|
||||
colonSpaceBytes = []byte(": ")
|
||||
openParenBytes = []byte("(")
|
||||
closeParenBytes = []byte(")")
|
||||
spaceBytes = []byte(" ")
|
||||
pointerChainBytes = []byte("->")
|
||||
nilAngleBytes = []byte("<nil>")
|
||||
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||
maxShortBytes = []byte("<max>")
|
||||
circularBytes = []byte("<already shown>")
|
||||
circularShortBytes = []byte("<shown>")
|
||||
invalidAngleBytes = []byte("<invalid>")
|
||||
openBracketBytes = []byte("[")
|
||||
closeBracketBytes = []byte("]")
|
||||
percentBytes = []byte("%")
|
||||
precisionBytes = []byte(".")
|
||||
openAngleBytes = []byte("<")
|
||||
closeAngleBytes = []byte(">")
|
||||
openMapBytes = []byte("map[")
|
||||
closeMapBytes = []byte("]")
|
||||
lenEqualsBytes = []byte("len=")
|
||||
capEqualsBytes = []byte("cap=")
|
||||
)
|
||||
|
||||
// hexDigits is used to map a decimal value to a hex digit.
|
||||
var hexDigits = "0123456789abcdef"
|
||||
|
||||
// catchPanic handles any panics that might occur during the handleMethods
|
||||
// calls.
|
||||
func catchPanic(w io.Writer, v reflect.Value) {
|
||||
if err := recover(); err != nil {
|
||||
w.Write(panicBytes)
|
||||
fmt.Fprintf(w, "%v", err)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// handleMethods attempts to call the Error and String methods on the underlying
|
||||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||
//
|
||||
// It handles panics in any called methods by catching and displaying the error
|
||||
// as the formatted value.
|
||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||
// We need an interface to check if the type implements the error or
|
||||
// Stringer interface. However, the reflect package won't give us an
|
||||
// interface on certain things like unexported struct fields in order
|
||||
// to enforce visibility rules. We use unsafe, when it's available,
|
||||
// to bypass these restrictions since this package does not mutate the
|
||||
// values.
|
||||
if !v.CanInterface() {
|
||||
if UnsafeDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
|
||||
// Choose whether or not to do error and Stringer interface lookups against
|
||||
// the base type or a pointer to the base type depending on settings.
|
||||
// Technically calling one of these methods with a pointer receiver can
|
||||
// mutate the value, however, types which choose to satisify an error or
|
||||
// Stringer interface with a pointer receiver should not be mutating their
|
||||
// state inside these interface methods.
|
||||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
|
||||
// Is it an error or Stringer?
|
||||
switch iface := v.Interface().(type) {
|
||||
case error:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.Error()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
|
||||
w.Write([]byte(iface.Error()))
|
||||
return true
|
||||
|
||||
case fmt.Stringer:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.String()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
w.Write([]byte(iface.String()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// printBool outputs a boolean value as true or false to Writer w.
|
||||
func printBool(w io.Writer, val bool) {
|
||||
if val {
|
||||
w.Write(trueBytes)
|
||||
} else {
|
||||
w.Write(falseBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// printInt outputs a signed integer value to Writer w.
|
||||
func printInt(w io.Writer, val int64, base int) {
|
||||
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||
}
|
||||
|
||||
// printUint outputs an unsigned integer value to Writer w.
|
||||
func printUint(w io.Writer, val uint64, base int) {
|
||||
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||
}
|
||||
|
||||
// printFloat outputs a floating point value using the specified precision,
|
||||
// which is expected to be 32 or 64bit, to Writer w.
|
||||
func printFloat(w io.Writer, val float64, precision int) {
|
||||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||
}
|
||||
|
||||
// printComplex outputs a complex value using the specified float precision
|
||||
// for the real and imaginary parts to Writer w.
|
||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
r := real(c)
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||
i := imag(c)
|
||||
if i >= 0 {
|
||||
w.Write(plusBytes)
|
||||
}
|
||||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||
w.Write(iBytes)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
num := uint64(p)
|
||||
if num == 0 {
|
||||
w.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||
buf := make([]byte, 18)
|
||||
|
||||
// It's simpler to construct the hex string right to left.
|
||||
base := uint64(16)
|
||||
i := len(buf) - 1
|
||||
for num >= base {
|
||||
buf[i] = hexDigits[num%base]
|
||||
num /= base
|
||||
i--
|
||||
}
|
||||
buf[i] = hexDigits[num]
|
||||
|
||||
// Add '0x' prefix.
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
|
||||
// Strip unused leading bytes.
|
||||
buf = buf[i:]
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||
// elements to be sorted.
|
||||
type valuesSorter struct {
|
||||
values []reflect.Value
|
||||
strings []string // either nil or same len and values
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||
// surrogate keys on which the data should be sorted. It uses flags in
|
||||
// ConfigState to decide if and how to populate those surrogate keys.
|
||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||
vs := &valuesSorter{values: values, cs: cs}
|
||||
if canSortSimply(vs.values[0].Kind()) {
|
||||
return vs
|
||||
}
|
||||
if !cs.DisableMethods {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
b := bytes.Buffer{}
|
||||
if !handleMethods(cs, &b, vs.values[i]) {
|
||||
vs.strings = nil
|
||||
break
|
||||
}
|
||||
vs.strings[i] = b.String()
|
||||
}
|
||||
}
|
||||
if vs.strings == nil && cs.SpewKeys {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||
// directly, or whether it should be considered for sorting by surrogate keys
|
||||
// (if the ConfigState allows it).
|
||||
func canSortSimply(kind reflect.Kind) bool {
|
||||
// This switch parallels valueSortLess, except for the default case.
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return true
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Uintptr:
|
||||
return true
|
||||
case reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of values in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Len() int {
|
||||
return len(s.values)
|
||||
}
|
||||
|
||||
// Swap swaps the values at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Swap(i, j int) {
|
||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||
if s.strings != nil {
|
||||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||
}
|
||||
}
|
||||
|
||||
// valueSortLess returns whether the first value should sort before the second
|
||||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||
// implementation.
|
||||
func valueSortLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.String:
|
||||
return a.String() < b.String()
|
||||
case reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Array:
|
||||
// Compare the contents of both arrays.
|
||||
l := a.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
av := a.Index(i)
|
||||
bv := b.Index(i)
|
||||
if av.Interface() == bv.Interface() {
|
||||
continue
|
||||
}
|
||||
return valueSortLess(av, bv)
|
||||
}
|
||||
}
|
||||
return a.String() < b.String()
|
||||
}
|
||||
|
||||
// Less returns whether the value at index i should sort before the
|
||||
// value at index j. It is part of the sort.Interface implementation.
|
||||
func (s *valuesSorter) Less(i, j int) bool {
|
||||
if s.strings == nil {
|
||||
return valueSortLess(s.values[i], s.values[j])
|
||||
}
|
||||
return s.strings[i] < s.strings[j]
|
||||
}
|
||||
|
||||
// sortValues is a sort function that handles both native types and any type that
|
||||
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||
// their Value.String() value to ensure display stability.
|
||||
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Sort(newValuesSorter(values, cs))
|
||||
}
|
||||
-298
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// custom type to test Stinger interface on non-pointer receiver.
|
||||
type stringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with non-pointer receivers.
|
||||
func (s stringer) String() string {
|
||||
return "stringer " + string(s)
|
||||
}
|
||||
|
||||
// custom type to test Stinger interface on pointer receiver.
|
||||
type pstringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with only pointer receivers.
|
||||
func (s *pstringer) String() string {
|
||||
return "stringer " + string(*s)
|
||||
}
|
||||
|
||||
// xref1 and xref2 are cross referencing structs for testing circular reference
|
||||
// detection.
|
||||
type xref1 struct {
|
||||
ps2 *xref2
|
||||
}
|
||||
type xref2 struct {
|
||||
ps1 *xref1
|
||||
}
|
||||
|
||||
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
|
||||
// reference for testing detection.
|
||||
type indirCir1 struct {
|
||||
ps2 *indirCir2
|
||||
}
|
||||
type indirCir2 struct {
|
||||
ps3 *indirCir3
|
||||
}
|
||||
type indirCir3 struct {
|
||||
ps1 *indirCir1
|
||||
}
|
||||
|
||||
// embed is used to test embedded structures.
|
||||
type embed struct {
|
||||
a string
|
||||
}
|
||||
|
||||
// embedwrap is used to test embedded structures.
|
||||
type embedwrap struct {
|
||||
*embed
|
||||
e *embed
|
||||
}
|
||||
|
||||
// panicer is used to intentionally cause a panic for testing spew properly
|
||||
// handles them
|
||||
type panicer int
|
||||
|
||||
func (p panicer) String() string {
|
||||
panic("test panic")
|
||||
}
|
||||
|
||||
// customError is used to test custom error interface invocation.
|
||||
type customError int
|
||||
|
||||
func (e customError) Error() string {
|
||||
return fmt.Sprintf("error: %d", int(e))
|
||||
}
|
||||
|
||||
// stringizeWants converts a slice of wanted test output into a format suitable
|
||||
// for a test error message.
|
||||
func stringizeWants(wants []string) string {
|
||||
s := ""
|
||||
for i, want := range wants {
|
||||
if i > 0 {
|
||||
s += fmt.Sprintf("want%d: %s", i+1, want)
|
||||
} else {
|
||||
s += "want: " + want
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// testFailed returns whether or not a test failed by checking if the result
|
||||
// of the test is in the slice of wanted strings.
|
||||
func testFailed(result string, wants []string) bool {
|
||||
for _, want := range wants {
|
||||
if result == want {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type sortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func (ss sortableStruct) String() string {
|
||||
return fmt.Sprintf("ss.%d", ss.x)
|
||||
}
|
||||
|
||||
type unsortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
type sortTestCase struct {
|
||||
input []reflect.Value
|
||||
expected []reflect.Value
|
||||
}
|
||||
|
||||
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
|
||||
getInterfaces := func(values []reflect.Value) []interface{} {
|
||||
interfaces := []interface{}{}
|
||||
for _, v := range values {
|
||||
interfaces = append(interfaces, v.Interface())
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
spew.SortValues(test.input, cs)
|
||||
// reflect.DeepEqual cannot really make sense of reflect.Value,
|
||||
// probably because of all the pointer tricks. For instance,
|
||||
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
|
||||
// instead.
|
||||
input := getInterfaces(test.input)
|
||||
expected := getInterfaces(test.expected)
|
||||
if !reflect.DeepEqual(input, expected) {
|
||||
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSortValues ensures the sort functionality for relect.Value based sorting
|
||||
// works as intended.
|
||||
func TestSortValues(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
embedA := v(embed{"a"})
|
||||
embedB := v(embed{"b"})
|
||||
embedC := v(embed{"c"})
|
||||
tests := []sortTestCase{
|
||||
// No values.
|
||||
{
|
||||
[]reflect.Value{},
|
||||
[]reflect.Value{},
|
||||
},
|
||||
// Bools.
|
||||
{
|
||||
[]reflect.Value{v(false), v(true), v(false)},
|
||||
[]reflect.Value{v(false), v(false), v(true)},
|
||||
},
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Uints.
|
||||
{
|
||||
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
|
||||
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
|
||||
},
|
||||
// Floats.
|
||||
{
|
||||
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
|
||||
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// Array
|
||||
{
|
||||
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
|
||||
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
|
||||
},
|
||||
// Uintptrs.
|
||||
{
|
||||
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
|
||||
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
// Note: not sorted - DisableMethods is set.
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
// Invalid.
|
||||
{
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using string methods.
|
||||
func TestSortValuesWithMethods(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using spew to stringify keys.
|
||||
func TestSortValuesWithSpew(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
-306
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ConfigState houses the configuration options used by spew to format and
|
||||
// display values. There is a global instance, Config, that is used to control
|
||||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||
// provides methods equivalent to the top-level functions.
|
||||
//
|
||||
// The zero value for ConfigState provides no indentation. You would typically
|
||||
// want to set it to a space or a tab.
|
||||
//
|
||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||
// with default settings. See the documentation of NewDefaultConfig for default
|
||||
// values.
|
||||
type ConfigState struct {
|
||||
// Indent specifies the string to use for each indentation level. The
|
||||
// global config instance that all top-level functions use set this to a
|
||||
// single space by default. If you would like more indentation, you might
|
||||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||
Indent string
|
||||
|
||||
// MaxDepth controls the maximum number of levels to descend into nested
|
||||
// data structures. The default, 0, means there is no limit.
|
||||
//
|
||||
// NOTE: Circular data structures are properly detected, so it is not
|
||||
// necessary to set this value unless you specifically want to limit deeply
|
||||
// nested data structures.
|
||||
MaxDepth int
|
||||
|
||||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||
// invoked for types that implement them.
|
||||
DisableMethods bool
|
||||
|
||||
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||
// error and Stringer interfaces on types which only accept a pointer
|
||||
// receiver when the current type is not a pointer.
|
||||
//
|
||||
// NOTE: This might be an unsafe action since calling one of these methods
|
||||
// with a pointer receiver could technically mutate the value, however,
|
||||
// in practice, types which choose to satisify an error or Stringer
|
||||
// interface with a pointer receiver should not be mutating their state
|
||||
// inside these interface methods. As a result, this option relies on
|
||||
// access to the unsafe package, so it will not have any effect when
|
||||
// running in environments without access to the unsafe package such as
|
||||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// DisablePointerAddresses specifies whether to disable the printing of
|
||||
// pointer addresses. This is useful when diffing data structures in tests.
|
||||
DisablePointerAddresses bool
|
||||
|
||||
// DisableCapacities specifies whether to disable the printing of capacities
|
||||
// for arrays, slices, maps and channels. This is useful when diffing
|
||||
// data structures in tests.
|
||||
DisableCapacities bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
// interface and return immediately instead of continuing to recurse into
|
||||
// the internals of the data type.
|
||||
//
|
||||
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||
// via the DisableMethods or DisablePointerMethods options.
|
||||
ContinueOnMethod bool
|
||||
|
||||
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||
// this to have a more deterministic, diffable output. Note that only
|
||||
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||
// that support the error or Stringer interfaces (if methods are
|
||||
// enabled) are supported, with other types sorted according to the
|
||||
// reflect.Value.String() output which guarantees display stability.
|
||||
SortKeys bool
|
||||
|
||||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||
// be spewed to strings and sorted by those strings. This is only
|
||||
// considered if SortKeys is true.
|
||||
SpewKeys bool
|
||||
}
|
||||
|
||||
// Config is the active configuration of the top-level functions.
|
||||
// The configuration can be changed by modifying the contents of spew.Config.
|
||||
var Config = ConfigState{Indent: " "}
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the formatted string as a value that satisfies error. See NewFormatter
|
||||
// for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
c.Printf, c.Println, or c.Printf.
|
||||
*/
|
||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(c, v)
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(c, w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by modifying the public members
|
||||
of c. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func (c *ConfigState) Dump(a ...interface{}) {
|
||||
fdump(c, os.Stdout, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(c, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a spew Formatter interface using
|
||||
// the ConfigState associated with s.
|
||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = newFormatter(c, arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||
//
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
func NewDefaultConfig() *ConfigState {
|
||||
return &ConfigState{Indent: " "}
|
||||
}
|
||||
-211
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging.
|
||||
|
||||
A quick overview of the additional features spew provides over the built-in
|
||||
printing facilities for Go data types are as follows:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
|
||||
There are two different approaches spew allows for dumping Go data structures:
|
||||
|
||||
* Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
|
||||
Quick Start
|
||||
|
||||
This section demonstrates how to quickly get started with spew. See the
|
||||
sections below for further details on formatting and configuration options.
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||
%#+v (adds types and pointer addresses):
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
via the spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
|
||||
* SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
Dump Usage
|
||||
|
||||
Simply call spew.Dump with a list of variables you want to dump:
|
||||
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
|
||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||
io.Writer. For example, to dump to standard error:
|
||||
|
||||
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||
|
||||
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Sample Dump Output
|
||||
|
||||
See the Dump example for details on the setup of the types and variables being
|
||||
shown here.
|
||||
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
(string) (len=3) "one": (bool) true
|
||||
}
|
||||
}
|
||||
|
||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||
command as shown.
|
||||
([]uint8) (len=32 cap=32) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
|
||||
Custom Formatter
|
||||
|
||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||
so that it integrates cleanly with standard fmt package printing functions. The
|
||||
formatter is useful for inline printing of smaller data types similar to the
|
||||
standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Custom Formatter Usage
|
||||
|
||||
The simplest way to make use of the spew custom formatter is to call one of the
|
||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||
functions have syntax you are most likely already familiar with:
|
||||
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Println(myVar, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
See the Index for the full list convenience functions.
|
||||
|
||||
Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
|
||||
See the Printf example for details on the setup of variables being shown
|
||||
here.
|
||||
|
||||
Errors
|
||||
|
||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||
detects them and handles them internally by printing the panic information
|
||||
inline with the output. Since spew is intended to provide deep pretty printing
|
||||
capabilities on structures, it intentionally does not return any errors.
|
||||
*/
|
||||
package spew
|
||||
-509
@@ -1,509 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||
// convert cgo types to uint8 slices for hexdumping.
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
type dumpState struct {
|
||||
w io.Writer
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
ignoreNextIndent bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// indent performs indentation according to the depth level and cs.Indent
|
||||
// option.
|
||||
func (d *dumpState) indent() {
|
||||
if d.ignoreNextIndent {
|
||||
d.ignoreNextIndent = false
|
||||
return
|
||||
}
|
||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range d.pointers {
|
||||
if depth >= d.depth {
|
||||
delete(d.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by dereferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
d.pointers[addr] = d.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type information.
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
d.w.Write([]byte(ve.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
d.w.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(d.w, addr)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
d.ignoreNextType = true
|
||||
d.dump(ve)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||
// Determine whether this type should be hex dumped or not. Also,
|
||||
// for types which should be hexdumped, try to use the underlying data
|
||||
// first, then fall back to trying to convert them to a uint8 slice.
|
||||
var buf []uint8
|
||||
doConvert := false
|
||||
doHexDump := false
|
||||
numEntries := v.Len()
|
||||
if numEntries > 0 {
|
||||
vt := v.Index(0).Type()
|
||||
vts := vt.String()
|
||||
switch {
|
||||
// C types that need to be converted.
|
||||
case cCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUnsignedCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUint8tCharRE.MatchString(vts):
|
||||
doConvert = true
|
||||
|
||||
// Try to use existing uint8 slices and fall back to converting
|
||||
// and copying if that fails.
|
||||
case vt.Kind() == reflect.Uint8:
|
||||
// We need an addressable interface to convert the type
|
||||
// to a byte slice. However, the reflect package won't
|
||||
// give us an interface on certain things like
|
||||
// unexported struct fields in order to enforce
|
||||
// visibility rules. We use unsafe, when available, to
|
||||
// bypass these restrictions since this package does not
|
||||
// mutate the values.
|
||||
vs := v
|
||||
if !vs.CanInterface() || !vs.CanAddr() {
|
||||
vs = unsafeReflectValue(vs)
|
||||
}
|
||||
if !UnsafeDisabled {
|
||||
vs = vs.Slice(0, numEntries)
|
||||
|
||||
// Use the existing uint8 slice if it can be
|
||||
// type asserted.
|
||||
iface := vs.Interface()
|
||||
if slice, ok := iface.([]uint8); ok {
|
||||
buf = slice
|
||||
doHexDump = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The underlying data needs to be converted if it can't
|
||||
// be type asserted to a uint8 slice.
|
||||
doConvert = true
|
||||
}
|
||||
|
||||
// Copy and convert the underlying type if needed.
|
||||
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||
// Convert and copy each element into a uint8 byte
|
||||
// slice.
|
||||
buf = make([]uint8, numEntries)
|
||||
for i := 0; i < numEntries; i++ {
|
||||
vv := v.Index(i)
|
||||
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||
}
|
||||
doHexDump = true
|
||||
}
|
||||
}
|
||||
|
||||
// Hexdump the entire slice as needed.
|
||||
if doHexDump {
|
||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||
str := indent + hex.Dump(buf)
|
||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||
str = strings.TrimRight(str, d.cs.Indent)
|
||||
d.w.Write([]byte(str))
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively call dump for each item.
|
||||
for i := 0; i < numEntries; i++ {
|
||||
d.dump(d.unpackValue(v.Index(i)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||
// value to figure out what kind of object we are dealing with and formats it
|
||||
// appropriately. It is a recursive function, however circular data structures
|
||||
// are detected and handled properly.
|
||||
func (d *dumpState) dump(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
d.w.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
d.indent()
|
||||
d.dumpPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !d.ignoreNextType {
|
||||
d.indent()
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write([]byte(v.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.ignoreNextType = false
|
||||
|
||||
// Display length and capacity if the built-in len and cap functions
|
||||
// work with the value's kind and the len/cap itself is non-zero.
|
||||
valueLen, valueCap := 0, 0
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||
valueLen, valueCap = v.Len(), v.Cap()
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.w.Write(capEqualsBytes)
|
||||
printInt(d.w, int64(valueCap), 10)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||
// is enabled
|
||||
if !d.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(d.w, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(d.w, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(d.w, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(d.w, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(d.w, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(d.w, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(d.w, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
d.dumpSlice(v)
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.String:
|
||||
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
keys := v.MapKeys()
|
||||
if d.cs.SortKeys {
|
||||
sortValues(keys, d.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
d.dump(d.unpackValue(key))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
numFields := v.NumField()
|
||||
for i := 0; i < numFields; i++ {
|
||||
d.indent()
|
||||
vtf := vt.Field(i)
|
||||
d.w.Write([]byte(vtf.Name))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.Field(i)))
|
||||
if i < (numFields - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(d.w, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(d.w, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it in case any new
|
||||
// types are added.
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(d.w, "%v", v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fdump is a helper function to consolidate the logic from the various public
|
||||
// methods which take varying writers and config states.
|
||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||
for _, arg := range a {
|
||||
if arg == nil {
|
||||
w.Write(interfaceBytes)
|
||||
w.Write(spaceBytes)
|
||||
w.Write(nilAngleBytes)
|
||||
w.Write(newlineBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
d := dumpState{w: w, cs: cs}
|
||||
d.pointers = make(map[uintptr]int)
|
||||
d.dump(reflect.ValueOf(arg))
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(&Config, w, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(&Config, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by an exported package global,
|
||||
spew.Config. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func Dump(a ...interface{}) {
|
||||
fdump(&Config, os.Stdout, a...)
|
||||
}
|
||||
-1042
File diff suppressed because it is too large
Load Diff
-101
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||
// command line. This means the cgo tests are only added (and hence run) when
|
||||
// specifially requested. This configuration is used because spew itself
|
||||
// does not require cgo to run even though it does handle certain cgo types
|
||||
// specially. Rather than forcing all clients to require cgo and an external
|
||||
// C compiler just to run the tests, this scheme makes them optional.
|
||||
// +build cgo,testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew/testdata"
|
||||
)
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// C char pointer.
|
||||
v := testdata.GetCgoCharPointer()
|
||||
nv := testdata.GetCgoNullCharPointer()
|
||||
pv := &v
|
||||
vcAddr := fmt.Sprintf("%p", v)
|
||||
vAddr := fmt.Sprintf("%p", pv)
|
||||
pvAddr := fmt.Sprintf("%p", &pv)
|
||||
vt := "*testdata._Ctype_char"
|
||||
vs := "116"
|
||||
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(nv, "("+vt+")(<nil>)\n")
|
||||
|
||||
// C char array.
|
||||
v2, v2l, v2c := testdata.GetCgoCharArray()
|
||||
v2Len := fmt.Sprintf("%d", v2l)
|
||||
v2Cap := fmt.Sprintf("%d", v2c)
|
||||
v2t := "[6]testdata._Ctype_char"
|
||||
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 32 00 " +
|
||||
" |test2.|\n}"
|
||||
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
|
||||
|
||||
// C unsigned char array.
|
||||
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
|
||||
v3Len := fmt.Sprintf("%d", v3l)
|
||||
v3Cap := fmt.Sprintf("%d", v3c)
|
||||
v3t := "[6]testdata._Ctype_unsignedchar"
|
||||
v3t2 := "[6]testdata._Ctype_uchar"
|
||||
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 33 00 " +
|
||||
" |test3.|\n}"
|
||||
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
|
||||
|
||||
// C signed char array.
|
||||
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
|
||||
v4Len := fmt.Sprintf("%d", v4l)
|
||||
v4Cap := fmt.Sprintf("%d", v4c)
|
||||
v4t := "[6]testdata._Ctype_schar"
|
||||
v4t2 := "testdata._Ctype_schar"
|
||||
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
|
||||
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
|
||||
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
|
||||
") 0\n}"
|
||||
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
|
||||
|
||||
// C uint8_t array.
|
||||
v5, v5l, v5c := testdata.GetCgoUint8tArray()
|
||||
v5Len := fmt.Sprintf("%d", v5l)
|
||||
v5Cap := fmt.Sprintf("%d", v5c)
|
||||
v5t := "[6]testdata._Ctype_uint8_t"
|
||||
v5t2 := "[6]testdata._Ctype_uchar"
|
||||
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 35 00 " +
|
||||
" |test5.|\n}"
|
||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n")
|
||||
|
||||
// C typedefed unsigned char array.
|
||||
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
||||
v6Len := fmt.Sprintf("%d", v6l)
|
||||
v6Cap := fmt.Sprintf("%d", v6c)
|
||||
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
||||
v6t2 := "[6]testdata._Ctype_uchar"
|
||||
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 36 00 " +
|
||||
" |test6.|\n}"
|
||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n")
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when either cgo is not supported or "-tags testcgo" is not added to the go
|
||||
// test command line. This file intentionally does not setup any cgo tests in
|
||||
// this scenario.
|
||||
// +build !cgo !testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// Don't add any tests for cgo since this file is only compiled when
|
||||
// there should not be any cgo tests.
|
||||
}
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Dump to dump variables to stdout.
|
||||
func ExampleDump() {
|
||||
// The following package level declarations are assumed for this example:
|
||||
/*
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
*/
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
f := Flag(5)
|
||||
b := []byte{
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32,
|
||||
}
|
||||
|
||||
// Dump!
|
||||
spew.Dump(s1, f, b)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Flag) Unknown flag (5)
|
||||
// ([]uint8) (len=34 cap=34) {
|
||||
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
// 00000020 31 32 |12|
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Printf to display a variable with a
|
||||
// format string and inline formatting.
|
||||
func ExamplePrintf() {
|
||||
// Create a double pointer to a uint 8.
|
||||
ui8 := uint8(5)
|
||||
pui8 := &ui8
|
||||
ppui8 := &pui8
|
||||
|
||||
// Create a circular data type.
|
||||
type circular struct {
|
||||
ui8 uint8
|
||||
c *circular
|
||||
}
|
||||
c := circular{ui8: 1}
|
||||
c.c = &c
|
||||
|
||||
// Print!
|
||||
spew.Printf("ppui8: %v\n", ppui8)
|
||||
spew.Printf("circular: %v\n", c)
|
||||
|
||||
// Output:
|
||||
// ppui8: <**>5
|
||||
// circular: {1 <*>{1 <*><shown>}}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use a ConfigState.
|
||||
func ExampleConfigState() {
|
||||
// Modify the indent level of the ConfigState only. The global
|
||||
// configuration is not modified.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
|
||||
// Output using the ConfigState instance.
|
||||
v := map[string]int{"one": 1}
|
||||
scs.Printf("v: %v\n", v)
|
||||
scs.Dump(v)
|
||||
|
||||
// Output:
|
||||
// v: map[one:1]
|
||||
// (map[string]int) (len=1) {
|
||||
// (string) (len=3) "one": (int) 1
|
||||
// }
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Dump to dump variables to
|
||||
// stdout
|
||||
func ExampleConfigState_Dump() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances with different indentation.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Dump(s1)
|
||||
scs2.Dump(s1)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Printf to display a variable
|
||||
// with a format string and inline formatting.
|
||||
func ExampleConfigState_Printf() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances and modify the method handling of the
|
||||
// first ConfigState only.
|
||||
scs := spew.NewDefaultConfig()
|
||||
scs2 := spew.NewDefaultConfig()
|
||||
scs.DisableMethods = true
|
||||
|
||||
// Alternatively
|
||||
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||
// scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// This is of type Flag which implements a Stringer and has raw value 1.
|
||||
f := flagTwo
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Printf("f: %v\n", f)
|
||||
scs2.Printf("f: %v\n", f)
|
||||
|
||||
// Output:
|
||||
// f: 1
|
||||
// f: flagTwo
|
||||
}
|
||||
-419
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||
const supportedFlags = "0-+# "
|
||||
|
||||
// formatState implements the fmt.Formatter interface and contains information
|
||||
// about the state of a formatting operation. The NewFormatter function can
|
||||
// be used to get a new Formatter which can be used directly as arguments
|
||||
// in standard fmt package printing calls.
|
||||
type formatState struct {
|
||||
value interface{}
|
||||
fs fmt.State
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// buildDefaultFormat recreates the original format string without precision
|
||||
// and width information to pass in to fmt.Sprintf in the case of an
|
||||
// unrecognized type. Unless new types are added to the language, this
|
||||
// function won't ever be called.
|
||||
func (f *formatState) buildDefaultFormat() (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteRune('v')
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// constructOrigFormat recreates the original format string including precision
|
||||
// and width information to pass along to the standard fmt package. This allows
|
||||
// automatic deferral of all format strings this package doesn't support.
|
||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
if width, ok := f.fs.Width(); ok {
|
||||
buf.WriteString(strconv.Itoa(width))
|
||||
}
|
||||
|
||||
if precision, ok := f.fs.Precision(); ok {
|
||||
buf.Write(precisionBytes)
|
||||
buf.WriteString(strconv.Itoa(precision))
|
||||
}
|
||||
|
||||
buf.WriteRune(verb)
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||
// ensures that types for values which have been unpacked from an interface
|
||||
// are displayed when the show types flag is also set.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface {
|
||||
f.ignoreNextType = false
|
||||
if !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (f *formatState) formatPtr(v reflect.Value) {
|
||||
// Display nil if top level pointer is nil.
|
||||
showTypes := f.fs.Flag('#')
|
||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range f.pointers {
|
||||
if depth >= f.depth {
|
||||
delete(f.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to possibly show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by derferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
f.pointers[addr] = f.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type or indirection level depending on flags.
|
||||
if showTypes && !f.ignoreNextType {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
f.fs.Write([]byte(ve.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
} else {
|
||||
if nilFound || cycleFound {
|
||||
indirects += strings.Count(ve.Type().String(), "*")
|
||||
}
|
||||
f.fs.Write(openAngleBytes)
|
||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||
f.fs.Write(closeAngleBytes)
|
||||
}
|
||||
|
||||
// Display pointer information depending on flags.
|
||||
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||
f.fs.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
f.fs.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(f.fs, addr)
|
||||
}
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
f.ignoreNextType = true
|
||||
f.format(ve)
|
||||
}
|
||||
}
|
||||
|
||||
// format is the main workhorse for providing the Formatter interface. It
|
||||
// uses the passed reflect value to figure out what kind of object we are
|
||||
// dealing with and formats it appropriately. It is a recursive function,
|
||||
// however circular data structures are detected and handled properly.
|
||||
func (f *formatState) format(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
f.fs.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
f.formatPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write([]byte(v.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
f.ignoreNextType = false
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods
|
||||
// flag is enabled.
|
||||
if !f.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(f.fs, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(f.fs, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(f.fs, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(f.fs, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(f.fs, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(f.fs, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(f.fs, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
f.fs.Write(openBracketBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
for i := 0; i < numEntries; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.Index(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBracketBytes)
|
||||
|
||||
case reflect.String:
|
||||
f.fs.Write([]byte(v.String()))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
f.fs.Write(openMapBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
keys := v.MapKeys()
|
||||
if f.cs.SortKeys {
|
||||
sortValues(keys, f.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(key))
|
||||
f.fs.Write(colonBytes)
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.MapIndex(key)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeMapBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
numFields := v.NumField()
|
||||
f.fs.Write(openBraceBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
for i := 0; i < numFields; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
vtf := vt.Field(i)
|
||||
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||
f.fs.Write([]byte(vtf.Name))
|
||||
f.fs.Write(colonBytes)
|
||||
}
|
||||
f.format(f.unpackValue(v.Field(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(f.fs, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it if any get added.
|
||||
default:
|
||||
format := f.buildDefaultFormat()
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(f.fs, format, v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(f.fs, format, v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||
// details.
|
||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||
f.fs = fs
|
||||
|
||||
// Use standard formatting for verbs that are not v.
|
||||
if verb != 'v' {
|
||||
format := f.constructOrigFormat(verb)
|
||||
fmt.Fprintf(fs, format, f.value)
|
||||
return
|
||||
}
|
||||
|
||||
if f.value == nil {
|
||||
if fs.Flag('#') {
|
||||
fs.Write(interfaceBytes)
|
||||
}
|
||||
fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
f.format(reflect.ValueOf(f.value))
|
||||
}
|
||||
|
||||
// newFormatter is a helper function to consolidate the logic from the various
|
||||
// public methods which take varying config states.
|
||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: cs}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
Printf, Println, or Fprintf.
|
||||
*/
|
||||
func NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(&Config, v)
|
||||
}
|
||||
-1558
File diff suppressed because it is too large
Load Diff
-84
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This test file is part of the spew package rather than than the spew_test
|
||||
package because it needs access to internals to properly test certain cases
|
||||
which are not possible via the public interface since they should never happen.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// dummyFmtState implements a fake fmt.State to use for testing invalid
|
||||
// reflect.Value handling. This is necessary because the fmt package catches
|
||||
// invalid values before invoking the formatter on them.
|
||||
type dummyFmtState struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Flag(f int) bool {
|
||||
return f == int('+')
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Precision() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Width() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// TestInvalidReflectValue ensures the dump and formatter code handles an
|
||||
// invalid reflect value properly. This needs access to internal state since it
|
||||
// should never happen in real code and therefore can't be tested via the public
|
||||
// API.
|
||||
func TestInvalidReflectValue(t *testing.T) {
|
||||
i := 1
|
||||
|
||||
// Dump invalid reflect value.
|
||||
v := new(reflect.Value)
|
||||
buf := new(bytes.Buffer)
|
||||
d := dumpState{w: buf, cs: &Config}
|
||||
d.dump(*v)
|
||||
s := buf.String()
|
||||
want := "<invalid>"
|
||||
if s != want {
|
||||
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter invalid reflect value.
|
||||
buf2 := new(dummyFmtState)
|
||||
f := formatState{value: *v, cs: &Config, fs: buf2}
|
||||
f.format(*v)
|
||||
s = buf2.String()
|
||||
want = "<invalid>"
|
||||
if s != want {
|
||||
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
}
|
||||
|
||||
// SortValues makes the internal sortValues function available to the test
|
||||
// package.
|
||||
func SortValues(values []reflect.Value, cs *ConfigState) {
|
||||
sortValues(values, cs)
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
/*
|
||||
This test file is part of the spew package rather than than the spew_test
|
||||
package because it needs access to internals to properly test certain cases
|
||||
which are not possible via the public interface since they should never happen.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
|
||||
// the maximum kind value which does not exist. This is needed to test the
|
||||
// fallback code which punts to the standard fmt library for new types that
|
||||
// might get added to the language.
|
||||
func changeKind(v *reflect.Value, readOnly bool) {
|
||||
flags := flagField(v)
|
||||
if readOnly {
|
||||
*flags |= flagRO
|
||||
} else {
|
||||
*flags &^= flagRO
|
||||
}
|
||||
*flags |= flagKindMask
|
||||
}
|
||||
|
||||
// TestAddedReflectValue tests functionaly of the dump and formatter code which
|
||||
// falls back to the standard fmt library for new types that might get added to
|
||||
// the language.
|
||||
func TestAddedReflectValue(t *testing.T) {
|
||||
i := 1
|
||||
|
||||
// Dump using a reflect.Value that is exported.
|
||||
v := reflect.ValueOf(int8(5))
|
||||
changeKind(&v, false)
|
||||
buf := new(bytes.Buffer)
|
||||
d := dumpState{w: buf, cs: &Config}
|
||||
d.dump(v)
|
||||
s := buf.String()
|
||||
want := "(int8) 5"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Dump using a reflect.Value that is not exported.
|
||||
changeKind(&v, true)
|
||||
buf.Reset()
|
||||
d.dump(v)
|
||||
s = buf.String()
|
||||
want = "(int8) <int8 Value>"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter using a reflect.Value that is exported.
|
||||
changeKind(&v, false)
|
||||
buf2 := new(dummyFmtState)
|
||||
f := formatState{value: v, cs: &Config, fs: buf2}
|
||||
f.format(v)
|
||||
s = buf2.String()
|
||||
want = "5"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter using a reflect.Value that is not exported.
|
||||
changeKind(&v, true)
|
||||
buf2.Reset()
|
||||
f = formatState{value: v, cs: &Config, fs: buf2}
|
||||
f.format(v)
|
||||
s = buf2.String()
|
||||
want = "<int8 Value>"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
}
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the formatted string as a value that satisfies error. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a default Formatter interface returned by NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a default spew Formatter interface.
|
||||
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = NewFormatter(arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
-320
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// spewFunc is used to identify which public function of the spew package or
|
||||
// ConfigState a test applies to.
|
||||
type spewFunc int
|
||||
|
||||
const (
|
||||
fCSFdump spewFunc = iota
|
||||
fCSFprint
|
||||
fCSFprintf
|
||||
fCSFprintln
|
||||
fCSPrint
|
||||
fCSPrintln
|
||||
fCSSdump
|
||||
fCSSprint
|
||||
fCSSprintf
|
||||
fCSSprintln
|
||||
fCSErrorf
|
||||
fCSNewFormatter
|
||||
fErrorf
|
||||
fFprint
|
||||
fFprintln
|
||||
fPrint
|
||||
fPrintln
|
||||
fSdump
|
||||
fSprint
|
||||
fSprintf
|
||||
fSprintln
|
||||
)
|
||||
|
||||
// Map of spewFunc values to names for pretty printing.
|
||||
var spewFuncStrings = map[spewFunc]string{
|
||||
fCSFdump: "ConfigState.Fdump",
|
||||
fCSFprint: "ConfigState.Fprint",
|
||||
fCSFprintf: "ConfigState.Fprintf",
|
||||
fCSFprintln: "ConfigState.Fprintln",
|
||||
fCSSdump: "ConfigState.Sdump",
|
||||
fCSPrint: "ConfigState.Print",
|
||||
fCSPrintln: "ConfigState.Println",
|
||||
fCSSprint: "ConfigState.Sprint",
|
||||
fCSSprintf: "ConfigState.Sprintf",
|
||||
fCSSprintln: "ConfigState.Sprintln",
|
||||
fCSErrorf: "ConfigState.Errorf",
|
||||
fCSNewFormatter: "ConfigState.NewFormatter",
|
||||
fErrorf: "spew.Errorf",
|
||||
fFprint: "spew.Fprint",
|
||||
fFprintln: "spew.Fprintln",
|
||||
fPrint: "spew.Print",
|
||||
fPrintln: "spew.Println",
|
||||
fSdump: "spew.Sdump",
|
||||
fSprint: "spew.Sprint",
|
||||
fSprintf: "spew.Sprintf",
|
||||
fSprintln: "spew.Sprintln",
|
||||
}
|
||||
|
||||
func (f spewFunc) String() string {
|
||||
if s, ok := spewFuncStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
|
||||
}
|
||||
|
||||
// spewTest is used to describe a test to be performed against the public
|
||||
// functions of the spew package or ConfigState.
|
||||
type spewTest struct {
|
||||
cs *spew.ConfigState
|
||||
f spewFunc
|
||||
format string
|
||||
in interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
// spewTests houses the tests to be performed against the public functions of
|
||||
// the spew package and ConfigState.
|
||||
//
|
||||
// These tests are only intended to ensure the public functions are exercised
|
||||
// and are intentionally not exhaustive of types. The exhaustive type
|
||||
// tests are handled in the dump and format tests.
|
||||
var spewTests []spewTest
|
||||
|
||||
// redirStdout is a helper function to return the standard output from f as a
|
||||
// byte slice.
|
||||
func redirStdout(f func()) ([]byte, error) {
|
||||
tempFile, err := ioutil.TempFile("", "ss-test")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileName := tempFile.Name()
|
||||
defer os.Remove(fileName) // Ignore error
|
||||
|
||||
origStdout := os.Stdout
|
||||
os.Stdout = tempFile
|
||||
f()
|
||||
os.Stdout = origStdout
|
||||
tempFile.Close()
|
||||
|
||||
return ioutil.ReadFile(fileName)
|
||||
}
|
||||
|
||||
func initSpewTests() {
|
||||
// Config states with various settings.
|
||||
scsDefault := spew.NewDefaultConfig()
|
||||
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
|
||||
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
|
||||
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
||||
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
|
||||
scsNoCap := &spew.ConfigState{DisableCapacities: true}
|
||||
|
||||
// Variables for tests on types which implement Stringer interface with and
|
||||
// without a pointer receiver.
|
||||
ts := stringer("test")
|
||||
tps := pstringer("test")
|
||||
|
||||
type ptrTester struct {
|
||||
s *struct{}
|
||||
}
|
||||
tptr := &ptrTester{s: &struct{}{}}
|
||||
|
||||
// depthTester is used to test max depth handling for structs, array, slices
|
||||
// and maps.
|
||||
type depthTester struct {
|
||||
ic indirCir1
|
||||
arr [1]string
|
||||
slice []string
|
||||
m map[string]int
|
||||
}
|
||||
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
||||
map[string]int{"one": 1}}
|
||||
|
||||
// Variable for tests on types which implement error interface.
|
||||
te := customError(10)
|
||||
|
||||
spewTests = []spewTest{
|
||||
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
|
||||
{scsDefault, fCSFprint, "", int16(32767), "32767"},
|
||||
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
|
||||
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
|
||||
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
|
||||
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
|
||||
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
|
||||
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
|
||||
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
|
||||
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
|
||||
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
|
||||
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
|
||||
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
|
||||
{scsDefault, fFprint, "", float32(3.14), "3.14"},
|
||||
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
|
||||
{scsDefault, fPrint, "", true, "true"},
|
||||
{scsDefault, fPrintln, "", false, "false\n"},
|
||||
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
|
||||
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
|
||||
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
|
||||
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
|
||||
{scsNoMethods, fCSFprint, "", ts, "test"},
|
||||
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
|
||||
{scsNoMethods, fCSFprint, "", tps, "test"},
|
||||
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
|
||||
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
|
||||
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
|
||||
{scsNoPmethods, fCSFprint, "", tps, "test"},
|
||||
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
|
||||
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
|
||||
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
|
||||
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
|
||||
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
|
||||
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
|
||||
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
|
||||
"(len=4) (stringer test) \"test\"\n"},
|
||||
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
|
||||
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
|
||||
"(error: 10) 10\n"},
|
||||
{scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"},
|
||||
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
|
||||
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
|
||||
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
|
||||
}
|
||||
}
|
||||
|
||||
// TestSpew executes all of the tests described by spewTests.
|
||||
func TestSpew(t *testing.T) {
|
||||
initSpewTests()
|
||||
|
||||
t.Logf("Running %d tests", len(spewTests))
|
||||
for i, test := range spewTests {
|
||||
buf := new(bytes.Buffer)
|
||||
switch test.f {
|
||||
case fCSFdump:
|
||||
test.cs.Fdump(buf, test.in)
|
||||
|
||||
case fCSFprint:
|
||||
test.cs.Fprint(buf, test.in)
|
||||
|
||||
case fCSFprintf:
|
||||
test.cs.Fprintf(buf, test.format, test.in)
|
||||
|
||||
case fCSFprintln:
|
||||
test.cs.Fprintln(buf, test.in)
|
||||
|
||||
case fCSPrint:
|
||||
b, err := redirStdout(func() { test.cs.Print(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fCSPrintln:
|
||||
b, err := redirStdout(func() { test.cs.Println(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fCSSdump:
|
||||
str := test.cs.Sdump(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprint:
|
||||
str := test.cs.Sprint(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprintf:
|
||||
str := test.cs.Sprintf(test.format, test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprintln:
|
||||
str := test.cs.Sprintln(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSErrorf:
|
||||
err := test.cs.Errorf(test.format, test.in)
|
||||
buf.WriteString(err.Error())
|
||||
|
||||
case fCSNewFormatter:
|
||||
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
|
||||
|
||||
case fErrorf:
|
||||
err := spew.Errorf(test.format, test.in)
|
||||
buf.WriteString(err.Error())
|
||||
|
||||
case fFprint:
|
||||
spew.Fprint(buf, test.in)
|
||||
|
||||
case fFprintln:
|
||||
spew.Fprintln(buf, test.in)
|
||||
|
||||
case fPrint:
|
||||
b, err := redirStdout(func() { spew.Print(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fPrintln:
|
||||
b, err := redirStdout(func() { spew.Println(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fSdump:
|
||||
str := spew.Sdump(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprint:
|
||||
str := spew.Sprint(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprintf:
|
||||
str := spew.Sprintf(test.format, test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprintln:
|
||||
str := spew.Sprintln(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
default:
|
||||
t.Errorf("%v #%d unrecognized function", test.f, i)
|
||||
continue
|
||||
}
|
||||
s := buf.String()
|
||||
if test.want != s {
|
||||
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
|
||||
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
|
||||
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
|
||||
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
|
||||
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
|
||||
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
|
||||
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.8.x
|
||||
- tip
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
version = "0.0.9"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
version = "0.0.3"
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
# Color [](https://godoc.org/github.com/fatih/color) [](https://travis-ci.org/fatih/color)
|
||||
|
||||
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||
has support for Windows too! The API can be used in several ways, pick one that
|
||||
suits you.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/color
|
||||
```
|
||||
|
||||
Note that the `vendor` folder is here for stability. Remove the folder if you
|
||||
already have the dependencies in your GOPATH.
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard colors
|
||||
|
||||
```go
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
```
|
||||
|
||||
### Mix and reuse colors
|
||||
|
||||
```go
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
```
|
||||
|
||||
### Use your own output (io.Writer)
|
||||
|
||||
```go
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(writer, "This will print text in blue.")
|
||||
```
|
||||
|
||||
### Custom print functions (PrintFunc)
|
||||
|
||||
```go
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
```
|
||||
|
||||
### Custom fprint functions (FprintFunc)
|
||||
|
||||
```go
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, "Don't forget this...")
|
||||
```
|
||||
|
||||
### Insert into noncolor strings (SprintFunc)
|
||||
|
||||
```go
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
```
|
||||
|
||||
### Plug into existing code
|
||||
|
||||
```go
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
```
|
||||
|
||||
### Disable/Enable color
|
||||
|
||||
There might be a case where you want to explicitly disable/enable color output. the
|
||||
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||
(for example if the output were piped directly to `less`)
|
||||
|
||||
`Color` has support to disable/enable colors both globally and for single color
|
||||
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
|
||||
can easily disable the color output with:
|
||||
|
||||
```go
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
```
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
```go
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
```
|
||||
|
||||
## Todo
|
||||
|
||||
* Save/Return previous values
|
||||
* Evaluate fmt.Formatter interface
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||
|
||||
-603
@@ -1,603 +0,0 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. This is a global option and affects all colors. For more control
|
||||
// over each color block use the methods DisableColor() individually.
|
||||
NoColor = os.Getenv("TERM") == "dumb" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
|
||||
// Output defines the standard output of the print functions. By default
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Error = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{params: make([]Attribute, 0)}
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
func (c *Color) setWriter(w io.Writer) *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unsetWriter(w io.Writer) {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) prepend(value Attribute) {
|
||||
c.params = append(c.params, 0)
|
||||
copy(c.params[1:], c.params[0:])
|
||||
c.params[0] = value
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprint(w, a...)
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintf(w, format, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintln(w, a...)
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintln(Output, a...)
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user setted action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, attr := range c.params {
|
||||
if !c2.attrExists(attr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Color) attrExists(a Attribute) bool {
|
||||
for _, attr := range c.params {
|
||||
if attr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attribute) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = New(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
||||
-342
@@ -1,342 +0,0 @@
|
||||
package color
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// Testing colors is kinda different. First we test for given colors and their
|
||||
// escaped formatted results. Next we create some visual tests to be tested.
|
||||
// Each visual test includes the color name to be compared.
|
||||
func TestColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
NoColor = false
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
{text: "hblack", code: FgHiBlack},
|
||||
{text: "hred", code: FgHiRed},
|
||||
{text: "hgreen", code: FgHiGreen},
|
||||
{text: "hyellow", code: FgHiYellow},
|
||||
{text: "hblue", code: FgHiBlue},
|
||||
{text: "hmagent", code: FgHiMagenta},
|
||||
{text: "hcyan", code: FgHiCyan},
|
||||
{text: "hwhite", code: FgHiWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
New(c.code).Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
scannedLine := fmt.Sprintf("%q", line)
|
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||
escapedForm := fmt.Sprintf("%q", colored)
|
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||
|
||||
if scannedLine != escapedForm {
|
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
line := New(c.code).Sprintf("%s", c.text)
|
||||
scannedLine := fmt.Sprintf("%q", line)
|
||||
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||
escapedForm := fmt.Sprintf("%q", colored)
|
||||
|
||||
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||
|
||||
if scannedLine != escapedForm {
|
||||
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorEquals(t *testing.T) {
|
||||
fgblack1 := New(FgBlack)
|
||||
fgblack2 := New(FgBlack)
|
||||
bgblack := New(BgBlack)
|
||||
fgbgblack := New(FgBlack, BgBlack)
|
||||
fgblackbgred := New(FgBlack, BgRed)
|
||||
fgred := New(FgRed)
|
||||
bgred := New(BgRed)
|
||||
|
||||
if !fgblack1.Equals(fgblack2) {
|
||||
t.Error("Two black colors are not equal")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(bgblack) {
|
||||
t.Error("Fg and bg black colors are equal")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgbgblack) {
|
||||
t.Error("Fg black equals fg/bg black color")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgred) {
|
||||
t.Error("Fg black equals Fg red")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(bgred) {
|
||||
t.Error("Fg black equals Bg red")
|
||||
}
|
||||
|
||||
if fgblack1.Equals(fgblackbgred) {
|
||||
t.Error("Fg black equals fg black bg red")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoColor(t *testing.T) {
|
||||
rb := new(bytes.Buffer)
|
||||
Output = rb
|
||||
|
||||
testColors := []struct {
|
||||
text string
|
||||
code Attribute
|
||||
}{
|
||||
{text: "black", code: FgBlack},
|
||||
{text: "red", code: FgRed},
|
||||
{text: "green", code: FgGreen},
|
||||
{text: "yellow", code: FgYellow},
|
||||
{text: "blue", code: FgBlue},
|
||||
{text: "magent", code: FgMagenta},
|
||||
{text: "cyan", code: FgCyan},
|
||||
{text: "white", code: FgWhite},
|
||||
{text: "hblack", code: FgHiBlack},
|
||||
{text: "hred", code: FgHiRed},
|
||||
{text: "hgreen", code: FgHiGreen},
|
||||
{text: "hyellow", code: FgHiYellow},
|
||||
{text: "hblue", code: FgHiBlue},
|
||||
{text: "hmagent", code: FgHiMagenta},
|
||||
{text: "hcyan", code: FgHiCyan},
|
||||
{text: "hwhite", code: FgHiWhite},
|
||||
}
|
||||
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.DisableColor()
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
|
||||
// global check
|
||||
NoColor = true
|
||||
defer func() {
|
||||
NoColor = false
|
||||
}()
|
||||
for _, c := range testColors {
|
||||
p := New(c.code)
|
||||
p.Print(c.text)
|
||||
|
||||
line, _ := rb.ReadString('\n')
|
||||
if line != c.text {
|
||||
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestColorVisual(t *testing.T) {
|
||||
// First Visual Test
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
New(FgRed).Printf("red\t")
|
||||
New(BgRed).Print(" ")
|
||||
New(FgRed, Bold).Println(" red")
|
||||
|
||||
New(FgGreen).Printf("green\t")
|
||||
New(BgGreen).Print(" ")
|
||||
New(FgGreen, Bold).Println(" green")
|
||||
|
||||
New(FgYellow).Printf("yellow\t")
|
||||
New(BgYellow).Print(" ")
|
||||
New(FgYellow, Bold).Println(" yellow")
|
||||
|
||||
New(FgBlue).Printf("blue\t")
|
||||
New(BgBlue).Print(" ")
|
||||
New(FgBlue, Bold).Println(" blue")
|
||||
|
||||
New(FgMagenta).Printf("magenta\t")
|
||||
New(BgMagenta).Print(" ")
|
||||
New(FgMagenta, Bold).Println(" magenta")
|
||||
|
||||
New(FgCyan).Printf("cyan\t")
|
||||
New(BgCyan).Print(" ")
|
||||
New(FgCyan, Bold).Println(" cyan")
|
||||
|
||||
New(FgWhite).Printf("white\t")
|
||||
New(BgWhite).Print(" ")
|
||||
New(FgWhite, Bold).Println(" white")
|
||||
fmt.Println("")
|
||||
|
||||
// Second Visual test
|
||||
Black("black")
|
||||
Red("red")
|
||||
Green("green")
|
||||
Yellow("yellow")
|
||||
Blue("blue")
|
||||
Magenta("magenta")
|
||||
Cyan("cyan")
|
||||
White("white")
|
||||
HiBlack("hblack")
|
||||
HiRed("hred")
|
||||
HiGreen("hgreen")
|
||||
HiYellow("hyellow")
|
||||
HiBlue("hblue")
|
||||
HiMagenta("hmagenta")
|
||||
HiCyan("hcyan")
|
||||
HiWhite("hwhite")
|
||||
|
||||
// Third visual test
|
||||
fmt.Println()
|
||||
Set(FgBlue)
|
||||
fmt.Println("is this blue?")
|
||||
Unset()
|
||||
|
||||
Set(FgMagenta)
|
||||
fmt.Println("and this magenta?")
|
||||
Unset()
|
||||
|
||||
// Fourth Visual test
|
||||
fmt.Println()
|
||||
blue := New(FgBlue).PrintlnFunc()
|
||||
blue("blue text with custom print func")
|
||||
|
||||
red := New(FgRed).PrintfFunc()
|
||||
red("red text with a printf func: %d\n", 123)
|
||||
|
||||
put := New(FgYellow).SprintFunc()
|
||||
warn := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
notice := New(FgBlue).FprintFunc()
|
||||
notice(os.Stderr, "just a blue notice to stderr")
|
||||
|
||||
// Fifth Visual Test
|
||||
fmt.Println()
|
||||
|
||||
fmt.Fprintln(Output, BlackString("black"))
|
||||
fmt.Fprintln(Output, RedString("red"))
|
||||
fmt.Fprintln(Output, GreenString("green"))
|
||||
fmt.Fprintln(Output, YellowString("yellow"))
|
||||
fmt.Fprintln(Output, BlueString("blue"))
|
||||
fmt.Fprintln(Output, MagentaString("magenta"))
|
||||
fmt.Fprintln(Output, CyanString("cyan"))
|
||||
fmt.Fprintln(Output, WhiteString("white"))
|
||||
fmt.Fprintln(Output, HiBlackString("hblack"))
|
||||
fmt.Fprintln(Output, HiRedString("hred"))
|
||||
fmt.Fprintln(Output, HiGreenString("hgreen"))
|
||||
fmt.Fprintln(Output, HiYellowString("hyellow"))
|
||||
fmt.Fprintln(Output, HiBlueString("hblue"))
|
||||
fmt.Fprintln(Output, HiMagentaString("hmagenta"))
|
||||
fmt.Fprintln(Output, HiCyanString("hcyan"))
|
||||
fmt.Fprintln(Output, HiWhiteString("hwhite"))
|
||||
}
|
||||
|
||||
func TestNoFormat(t *testing.T) {
|
||||
fmt.Printf("%s %%s = ", BlackString("Black"))
|
||||
Black("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", RedString("Red"))
|
||||
Red("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", GreenString("Green"))
|
||||
Green("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", YellowString("Yellow"))
|
||||
Yellow("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", BlueString("Blue"))
|
||||
Blue("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", MagentaString("Magenta"))
|
||||
Magenta("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", CyanString("Cyan"))
|
||||
Cyan("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", WhiteString("White"))
|
||||
White("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiBlackString("HiBlack"))
|
||||
HiBlack("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiRedString("HiRed"))
|
||||
HiRed("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiGreenString("HiGreen"))
|
||||
HiGreen("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiYellowString("HiYellow"))
|
||||
HiYellow("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiBlueString("HiBlue"))
|
||||
HiBlue("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiMagentaString("HiMagenta"))
|
||||
HiMagenta("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiCyanString("HiCyan"))
|
||||
HiCyan("%s")
|
||||
|
||||
fmt.Printf("%s %%s = ", HiWhiteString("HiWhite"))
|
||||
HiWhite("%s")
|
||||
}
|
||||
|
||||
func TestNoFormatString(t *testing.T) {
|
||||
tests := []struct {
|
||||
f func(string, ...interface{}) string
|
||||
format string
|
||||
args []interface{}
|
||||
want string
|
||||
}{
|
||||
{BlackString, "%s", nil, "\x1b[30m%s\x1b[0m"},
|
||||
{RedString, "%s", nil, "\x1b[31m%s\x1b[0m"},
|
||||
{GreenString, "%s", nil, "\x1b[32m%s\x1b[0m"},
|
||||
{YellowString, "%s", nil, "\x1b[33m%s\x1b[0m"},
|
||||
{BlueString, "%s", nil, "\x1b[34m%s\x1b[0m"},
|
||||
{MagentaString, "%s", nil, "\x1b[35m%s\x1b[0m"},
|
||||
{CyanString, "%s", nil, "\x1b[36m%s\x1b[0m"},
|
||||
{WhiteString, "%s", nil, "\x1b[37m%s\x1b[0m"},
|
||||
{HiBlackString, "%s", nil, "\x1b[90m%s\x1b[0m"},
|
||||
{HiRedString, "%s", nil, "\x1b[91m%s\x1b[0m"},
|
||||
{HiGreenString, "%s", nil, "\x1b[92m%s\x1b[0m"},
|
||||
{HiYellowString, "%s", nil, "\x1b[93m%s\x1b[0m"},
|
||||
{HiBlueString, "%s", nil, "\x1b[94m%s\x1b[0m"},
|
||||
{HiMagentaString, "%s", nil, "\x1b[95m%s\x1b[0m"},
|
||||
{HiCyanString, "%s", nil, "\x1b[96m%s\x1b[0m"},
|
||||
{HiWhiteString, "%s", nil, "\x1b[97m%s\x1b[0m"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
s := fmt.Sprintf("%s", test.f(test.format, test.args...))
|
||||
if s != test.want {
|
||||
t.Errorf("[%d] want: %q, got: %q", i, test.want, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
Package color is an ANSI color package to output colorized or SGR defined
|
||||
output to the standard output. The API can be used in several way, pick one
|
||||
that suits you.
|
||||
|
||||
Use simple and default helper functions with predefined foreground colors:
|
||||
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red")
|
||||
color.Yellow("Yellow color too!")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
// Hi-intensity colors
|
||||
color.HiGreen("Bright green color.")
|
||||
color.HiBlack("Bright black means gray..")
|
||||
color.HiWhite("Shiny white color!")
|
||||
|
||||
However there are times where custom color mixes are required. Below are some
|
||||
examples to create custom color objects and use the print functions of each
|
||||
separate color object.
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with White background.")
|
||||
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(myWriter, "This will print text in blue.")
|
||||
|
||||
You can create PrintXxx functions to simplify even more:
|
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("warning")
|
||||
red("error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("don't forget this...")
|
||||
|
||||
You can also FprintXxx functions to pass your own io.Writer:
|
||||
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, don't forget this...")
|
||||
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
|
||||
yellow := New(FgYellow).SprintFunc()
|
||||
red := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Printf("this %s rocks!\n", info("package"))
|
||||
|
||||
Windows support is enabled by default. All Print functions work as intended.
|
||||
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||
set the output to color.Output:
|
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the
|
||||
standard output to the given parameters. That way a rewrite of an existing
|
||||
code is not required.
|
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will be now in Yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.")
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
*/
|
||||
package color
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
# Setup a Global .gitignore for OS and editor generated files:
|
||||
# https://help.github.com/articles/ignoring-files
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
.vagrant
|
||||
*.sublime-project
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
fast_finish: true
|
||||
|
||||
before_script:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
|
||||
script:
|
||||
- go test -v --race ./...
|
||||
|
||||
after_script:
|
||||
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
|
||||
- test -z "$(golint ./... | tee /dev/stderr)"
|
||||
- go vet ./...
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# You can update this list using the following command:
|
||||
#
|
||||
# $ git shortlog -se | awk '{print $2 " " $3 " " $4}'
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Aaron L <aaron@bettercoder.net>
|
||||
Adrien Bustany <adrien@bustany.org>
|
||||
Amit Krishnan <amit.krishnan@oracle.com>
|
||||
Anmol Sethi <me@anmol.io>
|
||||
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
Case Nelson <case@teammating.com>
|
||||
Chris Howey <chris@howey.me> <howeyc@gmail.com>
|
||||
Christoffer Buchholz <christoffer.buchholz@gmail.com>
|
||||
Daniel Wagner-Hall <dawagner@gmail.com>
|
||||
Dave Cheney <dave@cheney.net>
|
||||
Evan Phoenix <evan@fallingsnow.net>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Hari haran <hariharan.uno@gmail.com>
|
||||
John C Barstow
|
||||
Kelvin Fo <vmirage@gmail.com>
|
||||
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
|
||||
Matt Layher <mdlayher@gmail.com>
|
||||
Nathan Youngman <git@nathany.com>
|
||||
Nickolai Zeldovich <nickolai@csail.mit.edu>
|
||||
Patrick <patrick@dropbox.com>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
Pawel Knap <pawelknap88@gmail.com>
|
||||
Pieter Droogendijk <pieter@binky.org.uk>
|
||||
Pursuit92 <JoshChase@techpursuit.net>
|
||||
Riku Voipio <riku.voipio@linaro.org>
|
||||
Rob Figueiredo <robfig@gmail.com>
|
||||
Rodrigo Chiossi <rodrigochiossi@gmail.com>
|
||||
Slawek Ligus <root@ooz.ie>
|
||||
Soge Zhang <zhssoge@gmail.com>
|
||||
Tiffany Jernigan <tiffany.jernigan@intel.com>
|
||||
Tilak Sharma <tilaks@google.com>
|
||||
Tom Payne <twpayne@gmail.com>
|
||||
Travis Cline <travis.cline@gmail.com>
|
||||
Tudor Golubenco <tudor.g@gmail.com>
|
||||
Vahe Khachikyan <vahe@live.ca>
|
||||
Yukang <moorekang@gmail.com>
|
||||
bronze1man <bronze1man@gmail.com>
|
||||
debrando <denis.brandolini@gmail.com>
|
||||
henrikedwards <henrik.edwards@gmail.com>
|
||||
铁哥 <guotie.9@gmail.com>
|
||||
-317
@@ -1,317 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.7 / 2018-01-09
|
||||
|
||||
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
|
||||
* Tests: Fix missing verb on format string (thanks @rchiossi)
|
||||
* Linux: Fix deadlock in Remove (thanks @aarondl)
|
||||
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
|
||||
* Docs: Moved FAQ into the README (thanks @vahe)
|
||||
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
|
||||
* Docs: replace references to OS X with macOS
|
||||
|
||||
## v1.4.2 / 2016-10-10
|
||||
|
||||
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
|
||||
|
||||
## v1.4.1 / 2016-10-04
|
||||
|
||||
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
|
||||
|
||||
## v1.4.0 / 2016-10-01
|
||||
|
||||
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
|
||||
|
||||
## v1.3.1 / 2016-06-28
|
||||
|
||||
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
|
||||
|
||||
## v1.3.0 / 2016-04-19
|
||||
|
||||
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
|
||||
|
||||
## v1.2.10 / 2016-03-02
|
||||
|
||||
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
|
||||
|
||||
## v1.2.9 / 2016-01-13
|
||||
|
||||
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
|
||||
|
||||
## v1.2.8 / 2015-12-17
|
||||
|
||||
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
|
||||
* inotify: fix race in test
|
||||
* enable race detection for continuous integration (Linux, Mac, Windows)
|
||||
|
||||
## v1.2.5 / 2015-10-17
|
||||
|
||||
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
|
||||
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
|
||||
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
|
||||
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
|
||||
|
||||
## v1.2.1 / 2015-10-14
|
||||
|
||||
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
|
||||
|
||||
## v1.2.0 / 2015-02-08
|
||||
|
||||
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
|
||||
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
|
||||
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
|
||||
|
||||
## v1.1.1 / 2015-02-05
|
||||
|
||||
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
|
||||
|
||||
## v1.1.0 / 2014-12-12
|
||||
|
||||
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
|
||||
* add low-level functions
|
||||
* only need to store flags on directories
|
||||
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
|
||||
* done can be an unbuffered channel
|
||||
* remove calls to os.NewSyscallError
|
||||
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
|
||||
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
|
||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||
|
||||
## v1.0.4 / 2014-09-07
|
||||
|
||||
* kqueue: add dragonfly to the build tags.
|
||||
* Rename source code files, rearrange code so exported APIs are at the top.
|
||||
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
|
||||
|
||||
## v1.0.3 / 2014-08-19
|
||||
|
||||
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
|
||||
|
||||
## v1.0.2 / 2014-08-17
|
||||
|
||||
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
|
||||
|
||||
## v1.0.0 / 2014-08-15
|
||||
|
||||
* [API] Remove AddWatch on Windows, use Add.
|
||||
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
|
||||
* Minor updates based on feedback from golint.
|
||||
|
||||
## dev / 2014-07-09
|
||||
|
||||
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
|
||||
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
|
||||
|
||||
## dev / 2014-07-04
|
||||
|
||||
* kqueue: fix incorrect mutex used in Close()
|
||||
* Update example to demonstrate usage of Op.
|
||||
|
||||
## dev / 2014-06-28
|
||||
|
||||
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
|
||||
* Fix for String() method on Event (thanks Alex Brainman)
|
||||
* Don't build on Plan 9 or Solaris (thanks @4ad)
|
||||
|
||||
## dev / 2014-06-21
|
||||
|
||||
* Events channel of type Event rather than *Event.
|
||||
* [internal] use syscall constants directly for inotify and kqueue.
|
||||
* [internal] kqueue: rename events to kevents and fileEvent to event.
|
||||
|
||||
## dev / 2014-06-19
|
||||
|
||||
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
|
||||
* [internal] remove cookie from Event struct (unused).
|
||||
* [internal] Event struct has the same definition across every OS.
|
||||
* [internal] remove internal watch and removeWatch methods.
|
||||
|
||||
## dev / 2014-06-12
|
||||
|
||||
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
|
||||
* [API] Pluralized channel names: Events and Errors.
|
||||
* [API] Renamed FileEvent struct to Event.
|
||||
* [API] Op constants replace methods like IsCreate().
|
||||
|
||||
## dev / 2014-06-12
|
||||
|
||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||
|
||||
## dev / 2014-05-23
|
||||
|
||||
* [API] Remove current implementation of WatchFlags.
|
||||
* current implementation doesn't take advantage of OS for efficiency
|
||||
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
|
||||
* no tests for the current implementation
|
||||
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
|
||||
|
||||
## v0.9.3 / 2014-12-31
|
||||
|
||||
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||
|
||||
## v0.9.2 / 2014-08-17
|
||||
|
||||
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
|
||||
## v0.9.1 / 2014-06-12
|
||||
|
||||
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||
|
||||
## v0.9.0 / 2014-01-17
|
||||
|
||||
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
|
||||
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
|
||||
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
|
||||
|
||||
## v0.8.12 / 2013-11-13
|
||||
|
||||
* [API] Remove FD_SET and friends from Linux adapter
|
||||
|
||||
## v0.8.11 / 2013-11-02
|
||||
|
||||
* [Doc] Add Changelog [#72][] (thanks @nathany)
|
||||
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
|
||||
|
||||
## v0.8.10 / 2013-10-19
|
||||
|
||||
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
|
||||
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
|
||||
* [Doc] specify OS-specific limits in README (thanks @debrando)
|
||||
|
||||
## v0.8.9 / 2013-09-08
|
||||
|
||||
* [Doc] Contributing (thanks @nathany)
|
||||
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
|
||||
* [Doc] GoCI badge in README (Linux only) [#60][]
|
||||
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
|
||||
|
||||
## v0.8.8 / 2013-06-17
|
||||
|
||||
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
|
||||
|
||||
## v0.8.7 / 2013-06-03
|
||||
|
||||
* [API] Make syscall flags internal
|
||||
* [Fix] inotify: ignore event changes
|
||||
* [Fix] race in symlink test [#45][] (reported by @srid)
|
||||
* [Fix] tests on Windows
|
||||
* lower case error messages
|
||||
|
||||
## v0.8.6 / 2013-05-23
|
||||
|
||||
* kqueue: Use EVT_ONLY flag on Darwin
|
||||
* [Doc] Update README with full example
|
||||
|
||||
## v0.8.5 / 2013-05-09
|
||||
|
||||
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
|
||||
|
||||
## v0.8.4 / 2013-04-07
|
||||
|
||||
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
|
||||
|
||||
## v0.8.3 / 2013-03-13
|
||||
|
||||
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
|
||||
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
|
||||
|
||||
## v0.8.2 / 2013-02-07
|
||||
|
||||
* [Doc] add Authors
|
||||
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
|
||||
|
||||
## v0.8.1 / 2013-01-09
|
||||
|
||||
* [Fix] Windows path separators
|
||||
* [Doc] BSD License
|
||||
|
||||
## v0.8.0 / 2012-11-09
|
||||
|
||||
* kqueue: directory watching improvements (thanks @vmirage)
|
||||
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
|
||||
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
|
||||
|
||||
## v0.7.4 / 2012-10-09
|
||||
|
||||
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
|
||||
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
|
||||
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
|
||||
* [Fix] kqueue: modify after recreation of file
|
||||
|
||||
## v0.7.3 / 2012-09-27
|
||||
|
||||
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
|
||||
* [Fix] kqueue: no longer get duplicate CREATE events
|
||||
|
||||
## v0.7.2 / 2012-09-01
|
||||
|
||||
* kqueue: events for created directories
|
||||
|
||||
## v0.7.1 / 2012-07-14
|
||||
|
||||
* [Fix] for renaming files
|
||||
|
||||
## v0.7.0 / 2012-07-02
|
||||
|
||||
* [Feature] FSNotify flags
|
||||
* [Fix] inotify: Added file name back to event path
|
||||
|
||||
## v0.6.0 / 2012-06-06
|
||||
|
||||
* kqueue: watch files after directory created (thanks @tmc)
|
||||
|
||||
## v0.5.1 / 2012-05-22
|
||||
|
||||
* [Fix] inotify: remove all watches before Close()
|
||||
|
||||
## v0.5.0 / 2012-05-03
|
||||
|
||||
* [API] kqueue: return errors during watch instead of sending over channel
|
||||
* kqueue: match symlink behavior on Linux
|
||||
* inotify: add `DELETE_SELF` (requested by @taralx)
|
||||
* [Fix] kqueue: handle EINTR (reported by @robfig)
|
||||
* [Doc] Godoc example [#1][] (thanks @davecheney)
|
||||
|
||||
## v0.4.0 / 2012-03-30
|
||||
|
||||
* Go 1 released: build with go tool
|
||||
* [Feature] Windows support using winfsnotify
|
||||
* Windows does not have attribute change notifications
|
||||
* Roll attribute notifications into IsModify
|
||||
|
||||
## v0.3.0 / 2012-02-19
|
||||
|
||||
* kqueue: add files when watch directory
|
||||
|
||||
## v0.2.0 / 2011-12-30
|
||||
|
||||
* update to latest Go weekly code
|
||||
|
||||
## v0.1.0 / 2011-10-19
|
||||
|
||||
* kqueue: add watch on file creation to match inotify
|
||||
* kqueue: create file event
|
||||
* inotify: ignore `IN_IGNORED` events
|
||||
* event String()
|
||||
* linux: common FileEvent functions
|
||||
* initial commit
|
||||
|
||||
[#79]: https://github.com/howeyc/fsnotify/pull/79
|
||||
[#77]: https://github.com/howeyc/fsnotify/pull/77
|
||||
[#72]: https://github.com/howeyc/fsnotify/issues/72
|
||||
[#71]: https://github.com/howeyc/fsnotify/issues/71
|
||||
[#70]: https://github.com/howeyc/fsnotify/issues/70
|
||||
[#63]: https://github.com/howeyc/fsnotify/issues/63
|
||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
||||
[#60]: https://github.com/howeyc/fsnotify/issues/60
|
||||
[#59]: https://github.com/howeyc/fsnotify/issues/59
|
||||
[#49]: https://github.com/howeyc/fsnotify/issues/49
|
||||
[#45]: https://github.com/howeyc/fsnotify/issues/45
|
||||
[#40]: https://github.com/howeyc/fsnotify/issues/40
|
||||
[#36]: https://github.com/howeyc/fsnotify/issues/36
|
||||
[#33]: https://github.com/howeyc/fsnotify/issues/33
|
||||
[#29]: https://github.com/howeyc/fsnotify/issues/29
|
||||
[#25]: https://github.com/howeyc/fsnotify/issues/25
|
||||
[#24]: https://github.com/howeyc/fsnotify/issues/24
|
||||
[#21]: https://github.com/howeyc/fsnotify/issues/21
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
## Issues
|
||||
|
||||
* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues).
|
||||
* Please indicate the platform you are using fsnotify on.
|
||||
* A code example to reproduce the problem is appreciated.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
### Contributor License Agreement
|
||||
|
||||
fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual).
|
||||
|
||||
Please indicate that you have signed the CLA in your pull request.
|
||||
|
||||
### How fsnotify is Developed
|
||||
|
||||
* Development is done on feature branches.
|
||||
* Tests are run on BSD, Linux, macOS and Windows.
|
||||
* Pull requests are reviewed and [applied to master][am] using [hub][].
|
||||
* Maintainers may modify or squash commits rather than asking contributors to.
|
||||
* To issue a new release, the maintainers will:
|
||||
* Update the CHANGELOG
|
||||
* Tag a version, which will become available through gopkg.in.
|
||||
|
||||
### How to Fork
|
||||
|
||||
For smooth sailing, always use the original import path. Installing with `go get` makes this easy.
|
||||
|
||||
1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`)
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Ensure everything works and the tests pass (see below)
|
||||
4. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
|
||||
Contribute upstream:
|
||||
|
||||
1. Fork fsnotify on GitHub
|
||||
2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`)
|
||||
3. Push to the branch (`git push fork my-new-feature`)
|
||||
4. Create a new Pull Request on GitHub
|
||||
|
||||
This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/).
|
||||
|
||||
### Testing
|
||||
|
||||
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
|
||||
|
||||
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
|
||||
|
||||
To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
|
||||
|
||||
* Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/)
|
||||
* Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder.
|
||||
* Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password)
|
||||
* Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`.
|
||||
* When you're done, you will want to halt or destroy the Vagrant boxes.
|
||||
|
||||
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
|
||||
|
||||
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
|
||||
|
||||
### Maintainers
|
||||
|
||||
Help maintaining fsnotify is welcome. To be a maintainer:
|
||||
|
||||
* Submit a pull request and sign the CLA as above.
|
||||
* You must be able to run the test suite on Mac, Windows, Linux and BSD.
|
||||
|
||||
To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][].
|
||||
|
||||
All code changes should be internal pull requests.
|
||||
|
||||
Releases are tagged using [Semantic Versioning](http://semver.org/).
|
||||
|
||||
[hub]: https://github.com/github/hub
|
||||
[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
Copyright (c) 2012 fsnotify Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
# File system notifications for Go
|
||||
|
||||
[](https://godoc.org/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify)
|
||||
|
||||
fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running:
|
||||
|
||||
```console
|
||||
go get -u golang.org/x/sys/...
|
||||
```
|
||||
|
||||
Cross platform: Windows, Linux, BSD and macOS.
|
||||
|
||||
|Adapter |OS |Status |
|
||||
|----------|----------|----------|
|
||||
|inotify |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
|
||||
|kqueue |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
|
||||
|ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|
||||
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|
||||
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
|
||||
|fanotify |Linux 2.6.37+ | |
|
||||
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
|
||||
|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
|
||||
|
||||
\* Android and iOS are untested.
|
||||
|
||||
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
|
||||
|
||||
## API stability
|
||||
|
||||
fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).
|
||||
|
||||
All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number.
|
||||
|
||||
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
|
||||
|
||||
## Example
|
||||
|
||||
See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
|
||||
|
||||
## FAQ
|
||||
|
||||
**When a file is moved to another directory is it still being watched?**
|
||||
|
||||
No (it shouldn't be, unless you are watching where it was moved to).
|
||||
|
||||
**When I watch a directory, are all subdirectories watched as well?**
|
||||
|
||||
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
|
||||
|
||||
**Do I have to watch the Error and Event channels in a separate goroutine?**
|
||||
|
||||
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
|
||||
|
||||
**Why am I receiving multiple events for the same file on OS X?**
|
||||
|
||||
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
|
||||
|
||||
**How many files can be watched at once?**
|
||||
|
||||
There are OS-specific limits as to how many watches can be created:
|
||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
|
||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
||||
|
||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
[#7]: https://github.com/howeyc/fsnotify/issues/7
|
||||
|
||||
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Related Projects
|
||||
|
||||
* [notify](https://github.com/rjeczalik/notify)
|
||||
* [fsevents](https://github.com/fsnotify/fsevents)
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
package fsnotify_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func ExampleNewWatcher() {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
log.Println("event:", event)
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
log.Println("modified file:", event.Name)
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
log.Println("error:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = watcher.Add("/tmp/foo")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build solaris
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Watcher watches a set of files, delivering events to a channel.
|
||||
type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
}
|
||||
|
||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add starts watching the named file or directory (non-recursively).
|
||||
func (w *Watcher) Add(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove stops watching the the named file or directory (non-recursively).
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
return nil
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
// Package fsnotify provides a platform-independent interface for file system notifications.
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Event represents a single file system notification.
|
||||
type Event struct {
|
||||
Name string // Relative path to the file or directory.
|
||||
Op Op // File operation that triggered the event.
|
||||
}
|
||||
|
||||
// Op describes a set of file operations.
|
||||
type Op uint32
|
||||
|
||||
// These are the generalized file operations that can trigger a notification.
|
||||
const (
|
||||
Create Op = 1 << iota
|
||||
Write
|
||||
Remove
|
||||
Rename
|
||||
Chmod
|
||||
)
|
||||
|
||||
func (op Op) String() string {
|
||||
// Use a buffer for efficient string concatenation
|
||||
var buffer bytes.Buffer
|
||||
|
||||
if op&Create == Create {
|
||||
buffer.WriteString("|CREATE")
|
||||
}
|
||||
if op&Remove == Remove {
|
||||
buffer.WriteString("|REMOVE")
|
||||
}
|
||||
if op&Write == Write {
|
||||
buffer.WriteString("|WRITE")
|
||||
}
|
||||
if op&Rename == Rename {
|
||||
buffer.WriteString("|RENAME")
|
||||
}
|
||||
if op&Chmod == Chmod {
|
||||
buffer.WriteString("|CHMOD")
|
||||
}
|
||||
if buffer.Len() == 0 {
|
||||
return ""
|
||||
}
|
||||
return buffer.String()[1:] // Strip leading pipe
|
||||
}
|
||||
|
||||
// String returns a string representation of the event in the form
|
||||
// "file: REMOVE|WRITE|..."
|
||||
func (e Event) String() string {
|
||||
return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
|
||||
}
|
||||
|
||||
// Common errors that can be reported by a watcher
|
||||
var ErrEventOverflow = errors.New("fsnotify queue overflow")
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEventStringWithValue(t *testing.T) {
|
||||
for opMask, expectedString := range map[Op]string{
|
||||
Chmod | Create: `"/usr/someFile": CREATE|CHMOD`,
|
||||
Rename: `"/usr/someFile": RENAME`,
|
||||
Remove: `"/usr/someFile": REMOVE`,
|
||||
Write | Chmod: `"/usr/someFile": WRITE|CHMOD`,
|
||||
} {
|
||||
event := Event{Name: "/usr/someFile", Op: opMask}
|
||||
if event.String() != expectedString {
|
||||
t.Fatalf("Expected %s, got: %v", expectedString, event.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventOpStringWithValue(t *testing.T) {
|
||||
expectedOpString := "WRITE|CHMOD"
|
||||
event := Event{Name: "someFile", Op: Write | Chmod}
|
||||
if event.Op.String() != expectedOpString {
|
||||
t.Fatalf("Expected %s, got: %v", expectedOpString, event.Op.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventOpStringWithNoValue(t *testing.T) {
|
||||
expectedOpString := ""
|
||||
event := Event{Name: "testFile", Op: 0}
|
||||
if event.Op.String() != expectedOpString {
|
||||
t.Fatalf("Expected %s, got: %v", expectedOpString, event.Op.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TestWatcherClose tests that the goroutine started by creating the watcher can be
|
||||
// signalled to return at any time, even if there is no goroutine listening on the events
|
||||
// or errors channels.
|
||||
func TestWatcherClose(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
name := tempMkFile(t, "")
|
||||
w := newWatcher(t)
|
||||
err := w.Add(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Remove(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Allow the watcher to receive the event.
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
-337
@@ -1,337 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Watcher watches a set of files, delivering events to a channel.
|
||||
type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
mu sync.Mutex // Map access
|
||||
fd int
|
||||
poller *fdPoller
|
||||
watches map[string]*watch // Map of inotify watches (key: path)
|
||||
paths map[int]string // Map of watched paths (key: watch descriptor)
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
doneResp chan struct{} // Channel to respond to Close
|
||||
}
|
||||
|
||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
// Create inotify fd
|
||||
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
|
||||
if fd == -1 {
|
||||
return nil, errno
|
||||
}
|
||||
// Create epoll
|
||||
poller, err := newFdPoller(fd)
|
||||
if err != nil {
|
||||
unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
w := &Watcher{
|
||||
fd: fd,
|
||||
poller: poller,
|
||||
watches: make(map[string]*watch),
|
||||
paths: make(map[int]string),
|
||||
Events: make(chan Event),
|
||||
Errors: make(chan error),
|
||||
done: make(chan struct{}),
|
||||
doneResp: make(chan struct{}),
|
||||
}
|
||||
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *Watcher) isClosed() bool {
|
||||
select {
|
||||
case <-w.done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
if w.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 'close' signal to goroutine, and set the Watcher to closed.
|
||||
close(w.done)
|
||||
|
||||
// Wake up goroutine
|
||||
w.poller.wake()
|
||||
|
||||
// Wait for goroutine to close
|
||||
<-w.doneResp
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add starts watching the named file or directory (non-recursively).
|
||||
func (w *Watcher) Add(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
if w.isClosed() {
|
||||
return errors.New("inotify instance already closed")
|
||||
}
|
||||
|
||||
const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
|
||||
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
|
||||
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
|
||||
|
||||
var flags uint32 = agnosticEvents
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
watchEntry := w.watches[name]
|
||||
if watchEntry != nil {
|
||||
flags |= watchEntry.flags | unix.IN_MASK_ADD
|
||||
}
|
||||
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
if wd == -1 {
|
||||
return errno
|
||||
}
|
||||
|
||||
if watchEntry == nil {
|
||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = name
|
||||
} else {
|
||||
watchEntry.wd = uint32(wd)
|
||||
watchEntry.flags = flags
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove stops watching the named file or directory (non-recursively).
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
|
||||
// Fetch the watch.
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
watch, ok := w.watches[name]
|
||||
|
||||
// Remove it from inotify.
|
||||
if !ok {
|
||||
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
|
||||
}
|
||||
|
||||
// We successfully removed the watch if InotifyRmWatch doesn't return an
|
||||
// error, we need to clean up our internal state to ensure it matches
|
||||
// inotify's kernel state.
|
||||
delete(w.paths, int(watch.wd))
|
||||
delete(w.watches, name)
|
||||
|
||||
// inotify_rm_watch will return EINVAL if the file has been deleted;
|
||||
// the inotify will already have been removed.
|
||||
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
|
||||
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
|
||||
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
|
||||
// by another thread and we have not received IN_IGNORE event.
|
||||
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
|
||||
if success == -1 {
|
||||
// TODO: Perhaps it's not helpful to return an error here in every case.
|
||||
// the only two possible errors are:
|
||||
// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
|
||||
// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
|
||||
// Watch descriptors are invalidated when they are removed explicitly or implicitly;
|
||||
// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type watch struct {
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
}
|
||||
|
||||
// readEvents reads from the inotify file descriptor, converts the
|
||||
// received events into Event objects and sends them via the Events channel
|
||||
func (w *Watcher) readEvents() {
|
||||
var (
|
||||
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
|
||||
n int // Number of bytes read with read()
|
||||
errno error // Syscall errno
|
||||
ok bool // For poller.wait
|
||||
)
|
||||
|
||||
defer close(w.doneResp)
|
||||
defer close(w.Errors)
|
||||
defer close(w.Events)
|
||||
defer unix.Close(w.fd)
|
||||
defer w.poller.close()
|
||||
|
||||
for {
|
||||
// See if we have been closed.
|
||||
if w.isClosed() {
|
||||
return
|
||||
}
|
||||
|
||||
ok, errno = w.poller.wait()
|
||||
if errno != nil {
|
||||
select {
|
||||
case w.Errors <- errno:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
n, errno = unix.Read(w.fd, buf[:])
|
||||
// If a signal interrupted execution, see if we've been asked to close, and try again.
|
||||
// http://man7.org/linux/man-pages/man7/signal.7.html :
|
||||
// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
|
||||
if errno == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
|
||||
// unix.Read might have been woken up by Close. If so, we're done.
|
||||
if w.isClosed() {
|
||||
return
|
||||
}
|
||||
|
||||
if n < unix.SizeofInotifyEvent {
|
||||
var err error
|
||||
if n == 0 {
|
||||
// If EOF is received. This should really never happen.
|
||||
err = io.EOF
|
||||
} else if n < 0 {
|
||||
// If an error occurred while reading.
|
||||
err = errno
|
||||
} else {
|
||||
// Read was too short.
|
||||
err = errors.New("notify: short read in readEvents()")
|
||||
}
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
var offset uint32
|
||||
// We don't know how many events we just read into the buffer
|
||||
// While the offset points to at least one whole event...
|
||||
for offset <= uint32(n-unix.SizeofInotifyEvent) {
|
||||
// Point "raw" to the event in the buffer
|
||||
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
||||
|
||||
mask := uint32(raw.Mask)
|
||||
nameLen := uint32(raw.Len)
|
||||
|
||||
if mask&unix.IN_Q_OVERFLOW != 0 {
|
||||
select {
|
||||
case w.Errors <- ErrEventOverflow:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If the event happened to the watched directory or the watched file, the kernel
|
||||
// doesn't append the filename to the event, but we would like to always fill the
|
||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
||||
// the "paths" map.
|
||||
w.mu.Lock()
|
||||
name, ok := w.paths[int(raw.Wd)]
|
||||
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
|
||||
// This is a sign to clean up the maps, otherwise we are no longer in sync
|
||||
// with the inotify kernel state which has already deleted the watch
|
||||
// automatically.
|
||||
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
||||
delete(w.paths, int(raw.Wd))
|
||||
delete(w.watches, name)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
if nameLen > 0 {
|
||||
// Point "bytes" at the first byte of the filename
|
||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
|
||||
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
|
||||
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
||||
}
|
||||
|
||||
event := newEvent(name, mask)
|
||||
|
||||
// Send the events that are not ignored on the events channel
|
||||
if !event.ignoreLinux(mask) {
|
||||
select {
|
||||
case w.Events <- event:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next event in the buffer
|
||||
offset += unix.SizeofInotifyEvent + nameLen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Certain types of events can be "ignored" and not sent over the Events
|
||||
// channel. Such as events marked ignore by the kernel, or MODIFY events
|
||||
// against files that do not exist.
|
||||
func (e *Event) ignoreLinux(mask uint32) bool {
|
||||
// Ignore anything the inotify API says to ignore
|
||||
if mask&unix.IN_IGNORED == unix.IN_IGNORED {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the event is not a DELETE or RENAME, the file must exist.
|
||||
// Otherwise the event is ignored.
|
||||
// *Note*: this was put in place because it was seen that a MODIFY
|
||||
// event was sent after the DELETE. This ignores that MODIFY and
|
||||
// assumes a DELETE will come or has come if the file doesn't exist.
|
||||
if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
|
||||
_, statErr := os.Lstat(e.Name)
|
||||
return os.IsNotExist(statErr)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// newEvent returns an platform-independent Event based on an inotify mask.
|
||||
func newEvent(name string, mask uint32) Event {
|
||||
e := Event{Name: name}
|
||||
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
|
||||
e.Op |= Create
|
||||
}
|
||||
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
|
||||
e.Op |= Remove
|
||||
}
|
||||
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
|
||||
e.Op |= Write
|
||||
}
|
||||
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
|
||||
e.Op |= Rename
|
||||
}
|
||||
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
|
||||
e.Op |= Chmod
|
||||
}
|
||||
return e
|
||||
}
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type fdPoller struct {
|
||||
fd int // File descriptor (as returned by the inotify_init() syscall)
|
||||
epfd int // Epoll file descriptor
|
||||
pipe [2]int // Pipe for waking up
|
||||
}
|
||||
|
||||
func emptyPoller(fd int) *fdPoller {
|
||||
poller := new(fdPoller)
|
||||
poller.fd = fd
|
||||
poller.epfd = -1
|
||||
poller.pipe[0] = -1
|
||||
poller.pipe[1] = -1
|
||||
return poller
|
||||
}
|
||||
|
||||
// Create a new inotify poller.
|
||||
// This creates an inotify handler, and an epoll handler.
|
||||
func newFdPoller(fd int) (*fdPoller, error) {
|
||||
var errno error
|
||||
poller := emptyPoller(fd)
|
||||
defer func() {
|
||||
if errno != nil {
|
||||
poller.close()
|
||||
}
|
||||
}()
|
||||
poller.fd = fd
|
||||
|
||||
// Create epoll fd
|
||||
poller.epfd, errno = unix.EpollCreate1(0)
|
||||
if poller.epfd == -1 {
|
||||
return nil, errno
|
||||
}
|
||||
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
|
||||
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
// Register inotify fd with epoll
|
||||
event := unix.EpollEvent{
|
||||
Fd: int32(poller.fd),
|
||||
Events: unix.EPOLLIN,
|
||||
}
|
||||
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event)
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
// Register pipe fd with epoll
|
||||
event = unix.EpollEvent{
|
||||
Fd: int32(poller.pipe[0]),
|
||||
Events: unix.EPOLLIN,
|
||||
}
|
||||
errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event)
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return poller, nil
|
||||
}
|
||||
|
||||
// Wait using epoll.
|
||||
// Returns true if something is ready to be read,
|
||||
// false if there is not.
|
||||
func (poller *fdPoller) wait() (bool, error) {
|
||||
// 3 possible events per fd, and 2 fds, makes a maximum of 6 events.
|
||||
// I don't know whether epoll_wait returns the number of events returned,
|
||||
// or the total number of events ready.
|
||||
// I decided to catch both by making the buffer one larger than the maximum.
|
||||
events := make([]unix.EpollEvent, 7)
|
||||
for {
|
||||
n, errno := unix.EpollWait(poller.epfd, events, -1)
|
||||
if n == -1 {
|
||||
if errno == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
return false, errno
|
||||
}
|
||||
if n == 0 {
|
||||
// If there are no events, try again.
|
||||
continue
|
||||
}
|
||||
if n > 6 {
|
||||
// This should never happen. More events were returned than should be possible.
|
||||
return false, errors.New("epoll_wait returned more events than I know what to do with")
|
||||
}
|
||||
ready := events[:n]
|
||||
epollhup := false
|
||||
epollerr := false
|
||||
epollin := false
|
||||
for _, event := range ready {
|
||||
if event.Fd == int32(poller.fd) {
|
||||
if event.Events&unix.EPOLLHUP != 0 {
|
||||
// This should not happen, but if it does, treat it as a wakeup.
|
||||
epollhup = true
|
||||
}
|
||||
if event.Events&unix.EPOLLERR != 0 {
|
||||
// If an error is waiting on the file descriptor, we should pretend
|
||||
// something is ready to read, and let unix.Read pick up the error.
|
||||
epollerr = true
|
||||
}
|
||||
if event.Events&unix.EPOLLIN != 0 {
|
||||
// There is data to read.
|
||||
epollin = true
|
||||
}
|
||||
}
|
||||
if event.Fd == int32(poller.pipe[0]) {
|
||||
if event.Events&unix.EPOLLHUP != 0 {
|
||||
// Write pipe descriptor was closed, by us. This means we're closing down the
|
||||
// watcher, and we should wake up.
|
||||
}
|
||||
if event.Events&unix.EPOLLERR != 0 {
|
||||
// If an error is waiting on the pipe file descriptor.
|
||||
// This is an absolute mystery, and should never ever happen.
|
||||
return false, errors.New("Error on the pipe descriptor.")
|
||||
}
|
||||
if event.Events&unix.EPOLLIN != 0 {
|
||||
// This is a regular wakeup, so we have to clear the buffer.
|
||||
err := poller.clearWake()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if epollhup || epollerr || epollin {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Close the write end of the poller.
|
||||
func (poller *fdPoller) wake() error {
|
||||
buf := make([]byte, 1)
|
||||
n, errno := unix.Write(poller.pipe[1], buf)
|
||||
if n == -1 {
|
||||
if errno == unix.EAGAIN {
|
||||
// Buffer is full, poller will wake.
|
||||
return nil
|
||||
}
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (poller *fdPoller) clearWake() error {
|
||||
// You have to be woken up a LOT in order to get to 100!
|
||||
buf := make([]byte, 100)
|
||||
n, errno := unix.Read(poller.pipe[0], buf)
|
||||
if n == -1 {
|
||||
if errno == unix.EAGAIN {
|
||||
// Buffer is empty, someone else cleared our wake.
|
||||
return nil
|
||||
}
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close all poller file descriptors, but not the one passed to it.
|
||||
func (poller *fdPoller) close() {
|
||||
if poller.pipe[1] != -1 {
|
||||
unix.Close(poller.pipe[1])
|
||||
}
|
||||
if poller.pipe[0] != -1 {
|
||||
unix.Close(poller.pipe[0])
|
||||
}
|
||||
if poller.epfd != -1 {
|
||||
unix.Close(poller.epfd)
|
||||
}
|
||||
}
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type testFd [2]int
|
||||
|
||||
func makeTestFd(t *testing.T) testFd {
|
||||
var tfd testFd
|
||||
errno := unix.Pipe(tfd[:])
|
||||
if errno != nil {
|
||||
t.Fatalf("Failed to create pipe: %v", errno)
|
||||
}
|
||||
return tfd
|
||||
}
|
||||
|
||||
func (tfd testFd) fd() int {
|
||||
return tfd[0]
|
||||
}
|
||||
|
||||
func (tfd testFd) closeWrite(t *testing.T) {
|
||||
errno := unix.Close(tfd[1])
|
||||
if errno != nil {
|
||||
t.Fatalf("Failed to close write end of pipe: %v", errno)
|
||||
}
|
||||
}
|
||||
|
||||
func (tfd testFd) put(t *testing.T) {
|
||||
buf := make([]byte, 10)
|
||||
_, errno := unix.Write(tfd[1], buf)
|
||||
if errno != nil {
|
||||
t.Fatalf("Failed to write to pipe: %v", errno)
|
||||
}
|
||||
}
|
||||
|
||||
func (tfd testFd) get(t *testing.T) {
|
||||
buf := make([]byte, 10)
|
||||
_, errno := unix.Read(tfd[0], buf)
|
||||
if errno != nil {
|
||||
t.Fatalf("Failed to read from pipe: %v", errno)
|
||||
}
|
||||
}
|
||||
|
||||
func (tfd testFd) close() {
|
||||
unix.Close(tfd[1])
|
||||
unix.Close(tfd[0])
|
||||
}
|
||||
|
||||
func makePoller(t *testing.T) (testFd, *fdPoller) {
|
||||
tfd := makeTestFd(t)
|
||||
poller, err := newFdPoller(tfd.fd())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create poller: %v", err)
|
||||
}
|
||||
return tfd, poller
|
||||
}
|
||||
|
||||
func TestPollerWithBadFd(t *testing.T) {
|
||||
_, err := newFdPoller(-1)
|
||||
if err != unix.EBADF {
|
||||
t.Fatalf("Expected EBADF, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPollerWithData(t *testing.T) {
|
||||
tfd, poller := makePoller(t)
|
||||
defer tfd.close()
|
||||
defer poller.close()
|
||||
|
||||
tfd.put(t)
|
||||
ok, err := poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected poller to return true")
|
||||
}
|
||||
tfd.get(t)
|
||||
}
|
||||
|
||||
func TestPollerWithWakeup(t *testing.T) {
|
||||
tfd, poller := makePoller(t)
|
||||
defer tfd.close()
|
||||
defer poller.close()
|
||||
|
||||
err := poller.wake()
|
||||
if err != nil {
|
||||
t.Fatalf("wake failed: %v", err)
|
||||
}
|
||||
ok, err := poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("expected poller to return false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPollerWithClose(t *testing.T) {
|
||||
tfd, poller := makePoller(t)
|
||||
defer tfd.close()
|
||||
defer poller.close()
|
||||
|
||||
tfd.closeWrite(t)
|
||||
ok, err := poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected poller to return true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPollerWithWakeupAndData(t *testing.T) {
|
||||
tfd, poller := makePoller(t)
|
||||
defer tfd.close()
|
||||
defer poller.close()
|
||||
|
||||
tfd.put(t)
|
||||
err := poller.wake()
|
||||
if err != nil {
|
||||
t.Fatalf("wake failed: %v", err)
|
||||
}
|
||||
|
||||
// both data and wakeup
|
||||
ok, err := poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected poller to return true")
|
||||
}
|
||||
|
||||
// data is still in the buffer, wakeup is cleared
|
||||
ok, err = poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected poller to return true")
|
||||
}
|
||||
|
||||
tfd.get(t)
|
||||
// data is gone, only wakeup now
|
||||
err = poller.wake()
|
||||
if err != nil {
|
||||
t.Fatalf("wake failed: %v", err)
|
||||
}
|
||||
ok, err = poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("expected poller to return false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPollerConcurrent(t *testing.T) {
|
||||
tfd, poller := makePoller(t)
|
||||
defer tfd.close()
|
||||
defer poller.close()
|
||||
|
||||
oks := make(chan bool)
|
||||
live := make(chan bool)
|
||||
defer close(live)
|
||||
go func() {
|
||||
defer close(oks)
|
||||
for {
|
||||
ok, err := poller.wait()
|
||||
if err != nil {
|
||||
t.Fatalf("poller failed: %v", err)
|
||||
}
|
||||
oks <- ok
|
||||
if !<-live {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Try a write
|
||||
select {
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
case <-oks:
|
||||
t.Fatalf("poller did not wait")
|
||||
}
|
||||
tfd.put(t)
|
||||
if !<-oks {
|
||||
t.Fatalf("expected true")
|
||||
}
|
||||
tfd.get(t)
|
||||
live <- true
|
||||
|
||||
// Try a wakeup
|
||||
select {
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
case <-oks:
|
||||
t.Fatalf("poller did not wait")
|
||||
}
|
||||
err := poller.wake()
|
||||
if err != nil {
|
||||
t.Fatalf("wake failed: %v", err)
|
||||
}
|
||||
if <-oks {
|
||||
t.Fatalf("expected false")
|
||||
}
|
||||
live <- true
|
||||
|
||||
// Try a close
|
||||
select {
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
case <-oks:
|
||||
t.Fatalf("poller did not wait")
|
||||
}
|
||||
tfd.closeWrite(t)
|
||||
if !<-oks {
|
||||
t.Fatalf("expected true")
|
||||
}
|
||||
tfd.get(t)
|
||||
}
|
||||
-449
@@ -1,449 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInotifyCloseRightAway(t *testing.T) {
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher")
|
||||
}
|
||||
|
||||
// Close immediately; it won't even reach the first unix.Read.
|
||||
w.Close()
|
||||
|
||||
// Wait for the close to complete.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
isWatcherReallyClosed(t, w)
|
||||
}
|
||||
|
||||
func TestInotifyCloseSlightlyLater(t *testing.T) {
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher")
|
||||
}
|
||||
|
||||
// Wait until readEvents has reached unix.Read, and Close.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
w.Close()
|
||||
|
||||
// Wait for the close to complete.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
isWatcherReallyClosed(t, w)
|
||||
}
|
||||
|
||||
func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) {
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher")
|
||||
}
|
||||
w.Add(testDir)
|
||||
|
||||
// Wait until readEvents has reached unix.Read, and Close.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
w.Close()
|
||||
|
||||
// Wait for the close to complete.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
isWatcherReallyClosed(t, w)
|
||||
}
|
||||
|
||||
func TestInotifyCloseAfterRead(t *testing.T) {
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher")
|
||||
}
|
||||
|
||||
err = w.Add(testDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add .")
|
||||
}
|
||||
|
||||
// Generate an event.
|
||||
os.Create(filepath.Join(testDir, "somethingSOMETHINGsomethingSOMETHING"))
|
||||
|
||||
// Wait for readEvents to read the event, then close the watcher.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
w.Close()
|
||||
|
||||
// Wait for the close to complete.
|
||||
<-time.After(50 * time.Millisecond)
|
||||
isWatcherReallyClosed(t, w)
|
||||
}
|
||||
|
||||
func isWatcherReallyClosed(t *testing.T, w *Watcher) {
|
||||
select {
|
||||
case err, ok := <-w.Errors:
|
||||
if ok {
|
||||
t.Fatalf("w.Errors is not closed; readEvents is still alive after closing (error: %v)", err)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("w.Errors would have blocked; readEvents is still alive!")
|
||||
}
|
||||
|
||||
select {
|
||||
case _, ok := <-w.Events:
|
||||
if ok {
|
||||
t.Fatalf("w.Events is not closed; readEvents is still alive after closing")
|
||||
}
|
||||
default:
|
||||
t.Fatalf("w.Events would have blocked; readEvents is still alive!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyCloseCreate(t *testing.T) {
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
err = w.Add(testDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add testDir: %v", err)
|
||||
}
|
||||
h, err := os.Create(filepath.Join(testDir, "testfile"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create file in testdir: %v", err)
|
||||
}
|
||||
h.Close()
|
||||
select {
|
||||
case _ = <-w.Events:
|
||||
case err := <-w.Errors:
|
||||
t.Fatalf("Error from watcher: %v", err)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
t.Fatalf("Took too long to wait for event")
|
||||
}
|
||||
|
||||
// At this point, we've received one event, so the goroutine is ready.
|
||||
// It's also blocking on unix.Read.
|
||||
// Now we try to swap the file descriptor under its nose.
|
||||
w.Close()
|
||||
w, err = NewWatcher()
|
||||
defer w.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create second watcher: %v", err)
|
||||
}
|
||||
|
||||
<-time.After(50 * time.Millisecond)
|
||||
err = w.Add(testDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding testDir again: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// This test verifies the watcher can keep up with file creations/deletions
|
||||
// when under load.
|
||||
func TestInotifyStress(t *testing.T) {
|
||||
maxNumToCreate := 1000
|
||||
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
testFilePrefix := filepath.Join(testDir, "testfile")
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
err = w.Add(testDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add testDir: %v", err)
|
||||
}
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
// The buffer ensures that the file generation goroutine is never blocked.
|
||||
errChan := make(chan error, 2*maxNumToCreate)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < maxNumToCreate; i++ {
|
||||
testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
|
||||
|
||||
handle, err := os.Create(testFile)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Create failed: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = handle.Close()
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Close failed: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If we delete a newly created file too quickly, inotify will skip the
|
||||
// create event and only send the delete event.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
for i := 0; i < maxNumToCreate; i++ {
|
||||
testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
|
||||
err = os.Remove(testFile)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Remove failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
close(doneChan)
|
||||
}()
|
||||
|
||||
creates := 0
|
||||
removes := 0
|
||||
|
||||
finished := false
|
||||
after := time.After(10 * time.Second)
|
||||
for !finished {
|
||||
select {
|
||||
case <-after:
|
||||
t.Fatalf("Not done")
|
||||
case <-doneChan:
|
||||
finished = true
|
||||
case err := <-errChan:
|
||||
t.Fatalf("Got an error from file creator goroutine: %v", err)
|
||||
case err := <-w.Errors:
|
||||
t.Fatalf("Got an error from watcher: %v", err)
|
||||
case evt := <-w.Events:
|
||||
if !strings.HasPrefix(evt.Name, testFilePrefix) {
|
||||
t.Fatalf("Got an event for an unknown file: %s", evt.Name)
|
||||
}
|
||||
if evt.Op == Create {
|
||||
creates++
|
||||
}
|
||||
if evt.Op == Remove {
|
||||
removes++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drain remaining events from channels
|
||||
count := 0
|
||||
for count < 10 {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
t.Fatalf("Got an error from file creator goroutine: %v", err)
|
||||
case err := <-w.Errors:
|
||||
t.Fatalf("Got an error from watcher: %v", err)
|
||||
case evt := <-w.Events:
|
||||
if !strings.HasPrefix(evt.Name, testFilePrefix) {
|
||||
t.Fatalf("Got an event for an unknown file: %s", evt.Name)
|
||||
}
|
||||
if evt.Op == Create {
|
||||
creates++
|
||||
}
|
||||
if evt.Op == Remove {
|
||||
removes++
|
||||
}
|
||||
count = 0
|
||||
default:
|
||||
count++
|
||||
// Give the watcher chances to fill the channels.
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
if creates-removes > 1 || creates-removes < -1 {
|
||||
t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes)
|
||||
}
|
||||
if creates < 50 {
|
||||
t.Fatalf("Expected at least 50 creates, got %d", creates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyRemoveTwice(t *testing.T) {
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
testFile := filepath.Join(testDir, "testfile")
|
||||
|
||||
handle, err := os.Create(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
handle.Close()
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
err = w.Add(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add testFile: %v", err)
|
||||
}
|
||||
|
||||
err = w.Remove(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("wanted successful remove but got: %v", err)
|
||||
}
|
||||
|
||||
err = w.Remove(testFile)
|
||||
if err == nil {
|
||||
t.Fatalf("no error on removing invalid file")
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if len(w.watches) != 0 {
|
||||
t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
|
||||
}
|
||||
if len(w.paths) != 0 {
|
||||
t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyInnerMapLength(t *testing.T) {
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
testFile := filepath.Join(testDir, "testfile")
|
||||
|
||||
handle, err := os.Create(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
handle.Close()
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
err = w.Add(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add testFile: %v", err)
|
||||
}
|
||||
go func() {
|
||||
for err := range w.Errors {
|
||||
t.Fatalf("error received: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = os.Remove(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to remove testFile: %v", err)
|
||||
}
|
||||
_ = <-w.Events // consume Remove event
|
||||
<-time.After(50 * time.Millisecond) // wait IN_IGNORE propagated
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if len(w.watches) != 0 {
|
||||
t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
|
||||
}
|
||||
if len(w.paths) != 0 {
|
||||
t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyOverflow(t *testing.T) {
|
||||
// We need to generate many more events than the
|
||||
// fs.inotify.max_queued_events sysctl setting.
|
||||
// We use multiple goroutines (one per directory)
|
||||
// to speed up file creation.
|
||||
numDirs := 128
|
||||
numFiles := 1024
|
||||
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
for dn := 0; dn < numDirs; dn++ {
|
||||
testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
|
||||
|
||||
err := os.Mkdir(testSubdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot create subdir: %v", err)
|
||||
}
|
||||
|
||||
err = w.Add(testSubdir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add subdir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
errChan := make(chan error, numDirs*numFiles)
|
||||
|
||||
for dn := 0; dn < numDirs; dn++ {
|
||||
testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
|
||||
|
||||
go func() {
|
||||
for fn := 0; fn < numFiles; fn++ {
|
||||
testFile := fmt.Sprintf("%s/%d", testSubdir, fn)
|
||||
|
||||
handle, err := os.Create(testFile)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Create failed: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = handle.Close()
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Close failed: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
creates := 0
|
||||
overflows := 0
|
||||
|
||||
after := time.After(10 * time.Second)
|
||||
for overflows == 0 && creates < numDirs*numFiles {
|
||||
select {
|
||||
case <-after:
|
||||
t.Fatalf("Not done")
|
||||
case err := <-errChan:
|
||||
t.Fatalf("Got an error from file creator goroutine: %v", err)
|
||||
case err := <-w.Errors:
|
||||
if err == ErrEventOverflow {
|
||||
overflows++
|
||||
} else {
|
||||
t.Fatalf("Got an error from watcher: %v", err)
|
||||
}
|
||||
case evt := <-w.Events:
|
||||
if !strings.HasPrefix(evt.Name, testDir) {
|
||||
t.Fatalf("Got an event for an unknown file: %s", evt.Name)
|
||||
}
|
||||
if evt.Op == Create {
|
||||
creates++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if creates == numDirs*numFiles {
|
||||
t.Fatalf("Could not trigger overflow")
|
||||
}
|
||||
|
||||
if overflows == 0 {
|
||||
t.Fatalf("No overflow and not enough creates (expected %d, got %d)",
|
||||
numDirs*numFiles, creates)
|
||||
}
|
||||
}
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// testExchangedataForWatcher tests the watcher with the exchangedata operation on macOS.
|
||||
//
|
||||
// This is widely used for atomic saves on macOS, e.g. TextMate and in Apple's NSDocument.
|
||||
//
|
||||
// See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html
|
||||
// Also see: https://github.com/textmate/textmate/blob/cd016be29489eba5f3c09b7b70b06da134dda550/Frameworks/io/src/swap_file_data.cc#L20
|
||||
func testExchangedataForWatcher(t *testing.T, watchDir bool) {
|
||||
// Create directory to watch
|
||||
testDir1 := tempMkdir(t)
|
||||
|
||||
// For the intermediate file
|
||||
testDir2 := tempMkdir(t)
|
||||
|
||||
defer os.RemoveAll(testDir1)
|
||||
defer os.RemoveAll(testDir2)
|
||||
|
||||
resolvedFilename := "TestFsnotifyEvents.file"
|
||||
|
||||
// TextMate does:
|
||||
//
|
||||
// 1. exchangedata (intermediate, resolved)
|
||||
// 2. unlink intermediate
|
||||
//
|
||||
// Let's try to simulate that:
|
||||
resolved := filepath.Join(testDir1, resolvedFilename)
|
||||
intermediate := filepath.Join(testDir2, resolvedFilename+"~")
|
||||
|
||||
// Make sure we create the file before we start watching
|
||||
createAndSyncFile(t, resolved)
|
||||
|
||||
watcher := newWatcher(t)
|
||||
|
||||
// Test both variants in isolation
|
||||
if watchDir {
|
||||
addWatch(t, watcher, testDir1)
|
||||
} else {
|
||||
addWatch(t, watcher, resolved)
|
||||
}
|
||||
|
||||
// Receive errors on the error channel on a separate goroutine
|
||||
go func() {
|
||||
for err := range watcher.Errors {
|
||||
t.Fatalf("error received: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Receive events on the event channel on a separate goroutine
|
||||
eventstream := watcher.Events
|
||||
var removeReceived counter
|
||||
var createReceived counter
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
for event := range eventstream {
|
||||
// Only count relevant events
|
||||
if event.Name == filepath.Clean(resolved) {
|
||||
if event.Op&Remove == Remove {
|
||||
removeReceived.increment()
|
||||
}
|
||||
if event.Op&Create == Create {
|
||||
createReceived.increment()
|
||||
}
|
||||
}
|
||||
t.Logf("event received: %s", event)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
|
||||
// Repeat to make sure the watched file/directory "survives" the REMOVE/CREATE loop.
|
||||
for i := 1; i <= 3; i++ {
|
||||
// The intermediate file is created in a folder outside the watcher
|
||||
createAndSyncFile(t, intermediate)
|
||||
|
||||
// 1. Swap
|
||||
if err := unix.Exchangedata(intermediate, resolved, 0); err != nil {
|
||||
t.Fatalf("[%d] exchangedata failed: %s", i, err)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// 2. Delete the intermediate file
|
||||
err := os.Remove(intermediate)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] remove %s failed: %s", i, intermediate, err)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
}
|
||||
|
||||
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// The events will be (CHMOD + REMOVE + CREATE) X 2. Let's focus on the last two:
|
||||
if removeReceived.value() < 3 {
|
||||
t.Fatal("fsnotify remove events have not been received after 500 ms")
|
||||
}
|
||||
|
||||
if createReceived.value() < 3 {
|
||||
t.Fatal("fsnotify create events have not been received after 500 ms")
|
||||
}
|
||||
|
||||
watcher.Close()
|
||||
t.Log("waiting for the event channel to become closed...")
|
||||
select {
|
||||
case <-done:
|
||||
t.Log("event channel closed")
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("event stream was not closed after 2 seconds")
|
||||
}
|
||||
}
|
||||
|
||||
// TestExchangedataInWatchedDir test exchangedata operation on file in watched dir.
|
||||
func TestExchangedataInWatchedDir(t *testing.T) {
|
||||
testExchangedataForWatcher(t, true)
|
||||
}
|
||||
|
||||
// TestExchangedataInWatchedDir test exchangedata operation on watched file.
|
||||
func TestExchangedataInWatchedFile(t *testing.T) {
|
||||
testExchangedataForWatcher(t, false)
|
||||
}
|
||||
|
||||
func createAndSyncFile(t *testing.T, filepath string) {
|
||||
f1, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
t.Fatalf("creating %s failed: %s", filepath, err)
|
||||
}
|
||||
f1.Sync()
|
||||
f1.Close()
|
||||
}
|
||||
-1237
File diff suppressed because it is too large
Load Diff
-521
@@ -1,521 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build freebsd openbsd netbsd dragonfly darwin
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Watcher watches a set of files, delivering events to a channel.
|
||||
type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
|
||||
kq int // File descriptor (as returned by the kqueue() syscall).
|
||||
|
||||
mu sync.Mutex // Protects access to watcher data
|
||||
watches map[string]int // Map of watched file descriptors (key: path).
|
||||
externalWatches map[string]bool // Map of watches added by user of the library.
|
||||
dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
|
||||
paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
|
||||
fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events).
|
||||
isClosed bool // Set to true when Close() is first called
|
||||
}
|
||||
|
||||
type pathInfo struct {
|
||||
name string
|
||||
isDir bool
|
||||
}
|
||||
|
||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
kq, err := kqueue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := &Watcher{
|
||||
kq: kq,
|
||||
watches: make(map[string]int),
|
||||
dirFlags: make(map[string]uint32),
|
||||
paths: make(map[int]pathInfo),
|
||||
fileExists: make(map[string]bool),
|
||||
externalWatches: make(map[string]bool),
|
||||
Events: make(chan Event),
|
||||
Errors: make(chan error),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
w.isClosed = true
|
||||
|
||||
// copy paths to remove while locked
|
||||
var pathsToRemove = make([]string, 0, len(w.watches))
|
||||
for name := range w.watches {
|
||||
pathsToRemove = append(pathsToRemove, name)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
// unlock before calling Remove, which also locks
|
||||
|
||||
for _, name := range pathsToRemove {
|
||||
w.Remove(name)
|
||||
}
|
||||
|
||||
// send a "quit" message to the reader goroutine
|
||||
close(w.done)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add starts watching the named file or directory (non-recursively).
|
||||
func (w *Watcher) Add(name string) error {
|
||||
w.mu.Lock()
|
||||
w.externalWatches[name] = true
|
||||
w.mu.Unlock()
|
||||
_, err := w.addWatch(name, noteAllEvents)
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove stops watching the the named file or directory (non-recursively).
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
w.mu.Lock()
|
||||
watchfd, ok := w.watches[name]
|
||||
w.mu.Unlock()
|
||||
if !ok {
|
||||
return fmt.Errorf("can't remove non-existent kevent watch for: %s", name)
|
||||
}
|
||||
|
||||
const registerRemove = unix.EV_DELETE
|
||||
if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unix.Close(watchfd)
|
||||
|
||||
w.mu.Lock()
|
||||
isDir := w.paths[watchfd].isDir
|
||||
delete(w.watches, name)
|
||||
delete(w.paths, watchfd)
|
||||
delete(w.dirFlags, name)
|
||||
w.mu.Unlock()
|
||||
|
||||
// Find all watched paths that are in this directory that are not external.
|
||||
if isDir {
|
||||
var pathsToRemove []string
|
||||
w.mu.Lock()
|
||||
for _, path := range w.paths {
|
||||
wdir, _ := filepath.Split(path.name)
|
||||
if filepath.Clean(wdir) == name {
|
||||
if !w.externalWatches[path.name] {
|
||||
pathsToRemove = append(pathsToRemove, path.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.mu.Unlock()
|
||||
for _, name := range pathsToRemove {
|
||||
// Since these are internal, not much sense in propagating error
|
||||
// to the user, as that will just confuse them with an error about
|
||||
// a path they did not explicitly watch themselves.
|
||||
w.Remove(name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
|
||||
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
|
||||
|
||||
// keventWaitTime to block on each read from kevent
|
||||
var keventWaitTime = durationToTimespec(100 * time.Millisecond)
|
||||
|
||||
// addWatch adds name to the watched file set.
|
||||
// The flags are interpreted as described in kevent(2).
|
||||
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
|
||||
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
var isDir bool
|
||||
// Make ./name and name equivalent
|
||||
name = filepath.Clean(name)
|
||||
|
||||
w.mu.Lock()
|
||||
if w.isClosed {
|
||||
w.mu.Unlock()
|
||||
return "", errors.New("kevent instance already closed")
|
||||
}
|
||||
watchfd, alreadyWatching := w.watches[name]
|
||||
// We already have a watch, but we can still override flags.
|
||||
if alreadyWatching {
|
||||
isDir = w.paths[watchfd].isDir
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
if !alreadyWatching {
|
||||
fi, err := os.Lstat(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Don't watch sockets.
|
||||
if fi.Mode()&os.ModeSocket == os.ModeSocket {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Don't watch named pipes.
|
||||
if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Follow Symlinks
|
||||
// Unfortunately, Linux can add bogus symlinks to watch list without
|
||||
// issue, and Windows can't do symlinks period (AFAIK). To maintain
|
||||
// consistency, we will act like everything is fine. There will simply
|
||||
// be no file events for broken symlinks.
|
||||
// Hence the returns of nil on errors.
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
name, err = filepath.EvalSymlinks(name)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
_, alreadyWatching = w.watches[name]
|
||||
w.mu.Unlock()
|
||||
|
||||
if alreadyWatching {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
fi, err = os.Lstat(name)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
watchfd, err = unix.Open(name, openMode, 0700)
|
||||
if watchfd == -1 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
isDir = fi.IsDir()
|
||||
}
|
||||
|
||||
const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE
|
||||
if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil {
|
||||
unix.Close(watchfd)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !alreadyWatching {
|
||||
w.mu.Lock()
|
||||
w.watches[name] = watchfd
|
||||
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
if isDir {
|
||||
// Watch the directory if it has not been watched before,
|
||||
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
|
||||
w.mu.Lock()
|
||||
|
||||
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
|
||||
(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
|
||||
// Store flags so this watch can be updated later
|
||||
w.dirFlags[name] = flags
|
||||
w.mu.Unlock()
|
||||
|
||||
if watchDir {
|
||||
if err := w.watchDirectoryFiles(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// readEvents reads from kqueue and converts the received kevents into
|
||||
// Event values that it sends down the Events channel.
|
||||
func (w *Watcher) readEvents() {
|
||||
eventBuffer := make([]unix.Kevent_t, 10)
|
||||
|
||||
loop:
|
||||
for {
|
||||
// See if there is a message on the "done" channel
|
||||
select {
|
||||
case <-w.done:
|
||||
break loop
|
||||
default:
|
||||
}
|
||||
|
||||
// Get new events
|
||||
kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
|
||||
// EINTR is okay, the syscall was interrupted before timeout expired.
|
||||
if err != nil && err != unix.EINTR {
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
case <-w.done:
|
||||
break loop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Flush the events we received to the Events channel
|
||||
for len(kevents) > 0 {
|
||||
kevent := &kevents[0]
|
||||
watchfd := int(kevent.Ident)
|
||||
mask := uint32(kevent.Fflags)
|
||||
w.mu.Lock()
|
||||
path := w.paths[watchfd]
|
||||
w.mu.Unlock()
|
||||
event := newEvent(path.name, mask)
|
||||
|
||||
if path.isDir && !(event.Op&Remove == Remove) {
|
||||
// Double check to make sure the directory exists. This can happen when
|
||||
// we do a rm -fr on a recursively watched folders and we receive a
|
||||
// modification event first but the folder has been deleted and later
|
||||
// receive the delete event
|
||||
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
|
||||
// mark is as delete event
|
||||
event.Op |= Remove
|
||||
}
|
||||
}
|
||||
|
||||
if event.Op&Rename == Rename || event.Op&Remove == Remove {
|
||||
w.Remove(event.Name)
|
||||
w.mu.Lock()
|
||||
delete(w.fileExists, event.Name)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
|
||||
w.sendDirectoryChangeEvents(event.Name)
|
||||
} else {
|
||||
// Send the event on the Events channel.
|
||||
select {
|
||||
case w.Events <- event:
|
||||
case <-w.done:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
if event.Op&Remove == Remove {
|
||||
// Look for a file that may have overwritten this.
|
||||
// For example, mv f1 f2 will delete f2, then create f2.
|
||||
if path.isDir {
|
||||
fileDir := filepath.Clean(event.Name)
|
||||
w.mu.Lock()
|
||||
_, found := w.watches[fileDir]
|
||||
w.mu.Unlock()
|
||||
if found {
|
||||
// make sure the directory exists before we watch for changes. When we
|
||||
// do a recursive watch and perform rm -fr, the parent directory might
|
||||
// have gone missing, ignore the missing directory and let the
|
||||
// upcoming delete event remove the watch from the parent directory.
|
||||
if _, err := os.Lstat(fileDir); err == nil {
|
||||
w.sendDirectoryChangeEvents(fileDir)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filePath := filepath.Clean(event.Name)
|
||||
if fileInfo, err := os.Lstat(filePath); err == nil {
|
||||
w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to next event
|
||||
kevents = kevents[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
err := unix.Close(w.kq)
|
||||
if err != nil {
|
||||
// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
close(w.Events)
|
||||
close(w.Errors)
|
||||
}
|
||||
|
||||
// newEvent returns an platform-independent Event based on kqueue Fflags.
|
||||
func newEvent(name string, mask uint32) Event {
|
||||
e := Event{Name: name}
|
||||
if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
|
||||
e.Op |= Remove
|
||||
}
|
||||
if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
|
||||
e.Op |= Write
|
||||
}
|
||||
if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
|
||||
e.Op |= Rename
|
||||
}
|
||||
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
|
||||
e.Op |= Chmod
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func newCreateEvent(name string) Event {
|
||||
return Event{Name: name, Op: Create}
|
||||
}
|
||||
|
||||
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
|
||||
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
||||
// Get all files
|
||||
files, err := ioutil.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fileInfo := range files {
|
||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
||||
filePath, err = w.internalWatch(filePath, fileInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.fileExists[filePath] = true
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendDirectoryEvents searches the directory for newly created files
|
||||
// and sends them over the event channel. This functionality is to have
|
||||
// the BSD version of fsnotify match Linux inotify which provides a
|
||||
// create event for files created in a watched directory.
|
||||
func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
|
||||
// Get all files
|
||||
files, err := ioutil.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Search for new files
|
||||
for _, fileInfo := range files {
|
||||
filePath := filepath.Join(dirPath, fileInfo.Name())
|
||||
err := w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
|
||||
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
|
||||
w.mu.Lock()
|
||||
_, doesExist := w.fileExists[filePath]
|
||||
w.mu.Unlock()
|
||||
if !doesExist {
|
||||
// Send create event
|
||||
select {
|
||||
case w.Events <- newCreateEvent(filePath):
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// like watchDirectoryFiles (but without doing another ReadDir)
|
||||
filePath, err = w.internalWatch(filePath, fileInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.fileExists[filePath] = true
|
||||
w.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
|
||||
if fileInfo.IsDir() {
|
||||
// mimic Linux providing delete events for subdirectories
|
||||
// but preserve the flags used if currently watching subdirectory
|
||||
w.mu.Lock()
|
||||
flags := w.dirFlags[name]
|
||||
w.mu.Unlock()
|
||||
|
||||
flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
|
||||
return w.addWatch(name, flags)
|
||||
}
|
||||
|
||||
// watch file to mimic Linux inotify
|
||||
return w.addWatch(name, noteAllEvents)
|
||||
}
|
||||
|
||||
// kqueue creates a new kernel event queue and returns a descriptor.
|
||||
func kqueue() (kq int, err error) {
|
||||
kq, err = unix.Kqueue()
|
||||
if kq == -1 {
|
||||
return kq, err
|
||||
}
|
||||
return kq, nil
|
||||
}
|
||||
|
||||
// register events with the queue
|
||||
func register(kq int, fds []int, flags int, fflags uint32) error {
|
||||
changes := make([]unix.Kevent_t, len(fds))
|
||||
|
||||
for i, fd := range fds {
|
||||
// SetKevent converts int to the platform-specific types:
|
||||
unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
|
||||
changes[i].Fflags = fflags
|
||||
}
|
||||
|
||||
// register the events
|
||||
success, err := unix.Kevent(kq, changes, nil, nil)
|
||||
if success == -1 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// read retrieves pending events, or waits until an event occurs.
|
||||
// A timeout of nil blocks indefinitely, while 0 polls the queue.
|
||||
func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) {
|
||||
n, err := unix.Kevent(kq, nil, events, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return events[0:n], nil
|
||||
}
|
||||
|
||||
// durationToTimespec prepares a timeout value
|
||||
func durationToTimespec(d time.Duration) unix.Timespec {
|
||||
return unix.NsecToTimespec(d.Nanoseconds())
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build freebsd openbsd netbsd dragonfly
|
||||
|
||||
package fsnotify
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin
|
||||
|
||||
package fsnotify
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// note: this constant is not defined on BSD
|
||||
const openMode = unix.O_EVTONLY
|
||||
-561
@@ -1,561 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package fsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Watcher watches a set of files, delivering events to a channel.
|
||||
type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
isClosed bool // Set to true when Close() is first called
|
||||
mu sync.Mutex // Map access
|
||||
port syscall.Handle // Handle to completion port
|
||||
watches watchMap // Map of watches (key: i-number)
|
||||
input chan *input // Inputs to the reader are sent on this channel
|
||||
quit chan chan<- error
|
||||
}
|
||||
|
||||
// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
|
||||
func NewWatcher() (*Watcher, error) {
|
||||
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
|
||||
if e != nil {
|
||||
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
|
||||
}
|
||||
w := &Watcher{
|
||||
port: port,
|
||||
watches: make(watchMap),
|
||||
input: make(chan *input, 1),
|
||||
Events: make(chan Event, 50),
|
||||
Errors: make(chan error),
|
||||
quit: make(chan chan<- error, 1),
|
||||
}
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Close removes all watches and closes the events channel.
|
||||
func (w *Watcher) Close() error {
|
||||
if w.isClosed {
|
||||
return nil
|
||||
}
|
||||
w.isClosed = true
|
||||
|
||||
// Send "quit" message to the reader goroutine
|
||||
ch := make(chan error)
|
||||
w.quit <- ch
|
||||
if err := w.wakeupReader(); err != nil {
|
||||
return err
|
||||
}
|
||||
return <-ch
|
||||
}
|
||||
|
||||
// Add starts watching the named file or directory (non-recursively).
|
||||
func (w *Watcher) Add(name string) error {
|
||||
if w.isClosed {
|
||||
return errors.New("watcher already closed")
|
||||
}
|
||||
in := &input{
|
||||
op: opAddWatch,
|
||||
path: filepath.Clean(name),
|
||||
flags: sysFSALLEVENTS,
|
||||
reply: make(chan error),
|
||||
}
|
||||
w.input <- in
|
||||
if err := w.wakeupReader(); err != nil {
|
||||
return err
|
||||
}
|
||||
return <-in.reply
|
||||
}
|
||||
|
||||
// Remove stops watching the the named file or directory (non-recursively).
|
||||
func (w *Watcher) Remove(name string) error {
|
||||
in := &input{
|
||||
op: opRemoveWatch,
|
||||
path: filepath.Clean(name),
|
||||
reply: make(chan error),
|
||||
}
|
||||
w.input <- in
|
||||
if err := w.wakeupReader(); err != nil {
|
||||
return err
|
||||
}
|
||||
return <-in.reply
|
||||
}
|
||||
|
||||
const (
|
||||
// Options for AddWatch
|
||||
sysFSONESHOT = 0x80000000
|
||||
sysFSONLYDIR = 0x1000000
|
||||
|
||||
// Events
|
||||
sysFSACCESS = 0x1
|
||||
sysFSALLEVENTS = 0xfff
|
||||
sysFSATTRIB = 0x4
|
||||
sysFSCLOSE = 0x18
|
||||
sysFSCREATE = 0x100
|
||||
sysFSDELETE = 0x200
|
||||
sysFSDELETESELF = 0x400
|
||||
sysFSMODIFY = 0x2
|
||||
sysFSMOVE = 0xc0
|
||||
sysFSMOVEDFROM = 0x40
|
||||
sysFSMOVEDTO = 0x80
|
||||
sysFSMOVESELF = 0x800
|
||||
|
||||
// Special events
|
||||
sysFSIGNORED = 0x8000
|
||||
sysFSQOVERFLOW = 0x4000
|
||||
)
|
||||
|
||||
func newEvent(name string, mask uint32) Event {
|
||||
e := Event{Name: name}
|
||||
if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
|
||||
e.Op |= Create
|
||||
}
|
||||
if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
|
||||
e.Op |= Remove
|
||||
}
|
||||
if mask&sysFSMODIFY == sysFSMODIFY {
|
||||
e.Op |= Write
|
||||
}
|
||||
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
|
||||
e.Op |= Rename
|
||||
}
|
||||
if mask&sysFSATTRIB == sysFSATTRIB {
|
||||
e.Op |= Chmod
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
const (
|
||||
opAddWatch = iota
|
||||
opRemoveWatch
|
||||
)
|
||||
|
||||
const (
|
||||
provisional uint64 = 1 << (32 + iota)
|
||||
)
|
||||
|
||||
type input struct {
|
||||
op int
|
||||
path string
|
||||
flags uint32
|
||||
reply chan error
|
||||
}
|
||||
|
||||
type inode struct {
|
||||
handle syscall.Handle
|
||||
volume uint32
|
||||
index uint64
|
||||
}
|
||||
|
||||
type watch struct {
|
||||
ov syscall.Overlapped
|
||||
ino *inode // i-number
|
||||
path string // Directory path
|
||||
mask uint64 // Directory itself is being watched with these notify flags
|
||||
names map[string]uint64 // Map of names being watched and their notify flags
|
||||
rename string // Remembers the old name while renaming a file
|
||||
buf [4096]byte
|
||||
}
|
||||
|
||||
type indexMap map[uint64]*watch
|
||||
type watchMap map[uint32]indexMap
|
||||
|
||||
func (w *Watcher) wakeupReader() error {
|
||||
e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)
|
||||
if e != nil {
|
||||
return os.NewSyscallError("PostQueuedCompletionStatus", e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDir(pathname string) (dir string, err error) {
|
||||
attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))
|
||||
if e != nil {
|
||||
return "", os.NewSyscallError("GetFileAttributes", e)
|
||||
}
|
||||
if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
dir = pathname
|
||||
} else {
|
||||
dir, _ = filepath.Split(pathname)
|
||||
dir = filepath.Clean(dir)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getIno(path string) (ino *inode, err error) {
|
||||
h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),
|
||||
syscall.FILE_LIST_DIRECTORY,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
nil, syscall.OPEN_EXISTING,
|
||||
syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)
|
||||
if e != nil {
|
||||
return nil, os.NewSyscallError("CreateFile", e)
|
||||
}
|
||||
var fi syscall.ByHandleFileInformation
|
||||
if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {
|
||||
syscall.CloseHandle(h)
|
||||
return nil, os.NewSyscallError("GetFileInformationByHandle", e)
|
||||
}
|
||||
ino = &inode{
|
||||
handle: h,
|
||||
volume: fi.VolumeSerialNumber,
|
||||
index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
|
||||
}
|
||||
return ino, nil
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (m watchMap) get(ino *inode) *watch {
|
||||
if i := m[ino.volume]; i != nil {
|
||||
return i[ino.index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (m watchMap) set(ino *inode, watch *watch) {
|
||||
i := m[ino.volume]
|
||||
if i == nil {
|
||||
i = make(indexMap)
|
||||
m[ino.volume] = i
|
||||
}
|
||||
i[ino.index] = watch
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
||||
dir, err := getDir(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flags&sysFSONLYDIR != 0 && pathname != dir {
|
||||
return nil
|
||||
}
|
||||
ino, err := getIno(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.mu.Lock()
|
||||
watchEntry := w.watches.get(ino)
|
||||
w.mu.Unlock()
|
||||
if watchEntry == nil {
|
||||
if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {
|
||||
syscall.CloseHandle(ino.handle)
|
||||
return os.NewSyscallError("CreateIoCompletionPort", e)
|
||||
}
|
||||
watchEntry = &watch{
|
||||
ino: ino,
|
||||
path: dir,
|
||||
names: make(map[string]uint64),
|
||||
}
|
||||
w.mu.Lock()
|
||||
w.watches.set(ino, watchEntry)
|
||||
w.mu.Unlock()
|
||||
flags |= provisional
|
||||
} else {
|
||||
syscall.CloseHandle(ino.handle)
|
||||
}
|
||||
if pathname == dir {
|
||||
watchEntry.mask |= flags
|
||||
} else {
|
||||
watchEntry.names[filepath.Base(pathname)] |= flags
|
||||
}
|
||||
if err = w.startRead(watchEntry); err != nil {
|
||||
return err
|
||||
}
|
||||
if pathname == dir {
|
||||
watchEntry.mask &= ^provisional
|
||||
} else {
|
||||
watchEntry.names[filepath.Base(pathname)] &= ^provisional
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) remWatch(pathname string) error {
|
||||
dir, err := getDir(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ino, err := getIno(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.mu.Lock()
|
||||
watch := w.watches.get(ino)
|
||||
w.mu.Unlock()
|
||||
if watch == nil {
|
||||
return fmt.Errorf("can't remove non-existent watch for: %s", pathname)
|
||||
}
|
||||
if pathname == dir {
|
||||
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
||||
watch.mask = 0
|
||||
} else {
|
||||
name := filepath.Base(pathname)
|
||||
w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
|
||||
delete(watch.names, name)
|
||||
}
|
||||
return w.startRead(watch)
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) deleteWatch(watch *watch) {
|
||||
for name, mask := range watch.names {
|
||||
if mask&provisional == 0 {
|
||||
w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
|
||||
}
|
||||
delete(watch.names, name)
|
||||
}
|
||||
if watch.mask != 0 {
|
||||
if watch.mask&provisional == 0 {
|
||||
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
||||
}
|
||||
watch.mask = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *Watcher) startRead(watch *watch) error {
|
||||
if e := syscall.CancelIo(watch.ino.handle); e != nil {
|
||||
w.Errors <- os.NewSyscallError("CancelIo", e)
|
||||
w.deleteWatch(watch)
|
||||
}
|
||||
mask := toWindowsFlags(watch.mask)
|
||||
for _, m := range watch.names {
|
||||
mask |= toWindowsFlags(m)
|
||||
}
|
||||
if mask == 0 {
|
||||
if e := syscall.CloseHandle(watch.ino.handle); e != nil {
|
||||
w.Errors <- os.NewSyscallError("CloseHandle", e)
|
||||
}
|
||||
w.mu.Lock()
|
||||
delete(w.watches[watch.ino.volume], watch.ino.index)
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
|
||||
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
|
||||
if e != nil {
|
||||
err := os.NewSyscallError("ReadDirectoryChanges", e)
|
||||
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
||||
// Watched directory was probably removed
|
||||
if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) {
|
||||
if watch.mask&sysFSONESHOT != 0 {
|
||||
watch.mask = 0
|
||||
}
|
||||
}
|
||||
err = nil
|
||||
}
|
||||
w.deleteWatch(watch)
|
||||
w.startRead(watch)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readEvents reads from the I/O completion port, converts the
|
||||
// received events into Event objects and sends them via the Events channel.
|
||||
// Entry point to the I/O thread.
|
||||
func (w *Watcher) readEvents() {
|
||||
var (
|
||||
n, key uint32
|
||||
ov *syscall.Overlapped
|
||||
)
|
||||
runtime.LockOSThread()
|
||||
|
||||
for {
|
||||
e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)
|
||||
watch := (*watch)(unsafe.Pointer(ov))
|
||||
|
||||
if watch == nil {
|
||||
select {
|
||||
case ch := <-w.quit:
|
||||
w.mu.Lock()
|
||||
var indexes []indexMap
|
||||
for _, index := range w.watches {
|
||||
indexes = append(indexes, index)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
for _, index := range indexes {
|
||||
for _, watch := range index {
|
||||
w.deleteWatch(watch)
|
||||
w.startRead(watch)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if e := syscall.CloseHandle(w.port); e != nil {
|
||||
err = os.NewSyscallError("CloseHandle", e)
|
||||
}
|
||||
close(w.Events)
|
||||
close(w.Errors)
|
||||
ch <- err
|
||||
return
|
||||
case in := <-w.input:
|
||||
switch in.op {
|
||||
case opAddWatch:
|
||||
in.reply <- w.addWatch(in.path, uint64(in.flags))
|
||||
case opRemoveWatch:
|
||||
in.reply <- w.remWatch(in.path)
|
||||
}
|
||||
default:
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch e {
|
||||
case syscall.ERROR_MORE_DATA:
|
||||
if watch == nil {
|
||||
w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")
|
||||
} else {
|
||||
// The i/o succeeded but the buffer is full.
|
||||
// In theory we should be building up a full packet.
|
||||
// In practice we can get away with just carrying on.
|
||||
n = uint32(unsafe.Sizeof(watch.buf))
|
||||
}
|
||||
case syscall.ERROR_ACCESS_DENIED:
|
||||
// Watched directory was probably removed
|
||||
w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
|
||||
w.deleteWatch(watch)
|
||||
w.startRead(watch)
|
||||
continue
|
||||
case syscall.ERROR_OPERATION_ABORTED:
|
||||
// CancelIo was called on this handle
|
||||
continue
|
||||
default:
|
||||
w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e)
|
||||
continue
|
||||
case nil:
|
||||
}
|
||||
|
||||
var offset uint32
|
||||
for {
|
||||
if n == 0 {
|
||||
w.Events <- newEvent("", sysFSQOVERFLOW)
|
||||
w.Errors <- errors.New("short read in readEvents()")
|
||||
break
|
||||
}
|
||||
|
||||
// Point "raw" to the event in the buffer
|
||||
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
|
||||
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
|
||||
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
|
||||
fullname := filepath.Join(watch.path, name)
|
||||
|
||||
var mask uint64
|
||||
switch raw.Action {
|
||||
case syscall.FILE_ACTION_REMOVED:
|
||||
mask = sysFSDELETESELF
|
||||
case syscall.FILE_ACTION_MODIFIED:
|
||||
mask = sysFSMODIFY
|
||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
||||
watch.rename = name
|
||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
||||
if watch.names[watch.rename] != 0 {
|
||||
watch.names[name] |= watch.names[watch.rename]
|
||||
delete(watch.names, watch.rename)
|
||||
mask = sysFSMOVESELF
|
||||
}
|
||||
}
|
||||
|
||||
sendNameEvent := func() {
|
||||
if w.sendEvent(fullname, watch.names[name]&mask) {
|
||||
if watch.names[name]&sysFSONESHOT != 0 {
|
||||
delete(watch.names, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
||||
sendNameEvent()
|
||||
}
|
||||
if raw.Action == syscall.FILE_ACTION_REMOVED {
|
||||
w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
|
||||
delete(watch.names, name)
|
||||
}
|
||||
if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {
|
||||
if watch.mask&sysFSONESHOT != 0 {
|
||||
watch.mask = 0
|
||||
}
|
||||
}
|
||||
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
|
||||
fullname = filepath.Join(watch.path, watch.rename)
|
||||
sendNameEvent()
|
||||
}
|
||||
|
||||
// Move to the next event in the buffer
|
||||
if raw.NextEntryOffset == 0 {
|
||||
break
|
||||
}
|
||||
offset += raw.NextEntryOffset
|
||||
|
||||
// Error!
|
||||
if offset >= n {
|
||||
w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.startRead(watch); err != nil {
|
||||
w.Errors <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Watcher) sendEvent(name string, mask uint64) bool {
|
||||
if mask == 0 {
|
||||
return false
|
||||
}
|
||||
event := newEvent(name, uint32(mask))
|
||||
select {
|
||||
case ch := <-w.quit:
|
||||
w.quit <- ch
|
||||
case w.Events <- event:
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func toWindowsFlags(mask uint64) uint32 {
|
||||
var m uint32
|
||||
if mask&sysFSACCESS != 0 {
|
||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS
|
||||
}
|
||||
if mask&sysFSMODIFY != 0 {
|
||||
m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE
|
||||
}
|
||||
if mask&sysFSATTRIB != 0 {
|
||||
m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES
|
||||
}
|
||||
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
|
||||
m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func toFSnotifyFlags(action uint32) uint64 {
|
||||
switch action {
|
||||
case syscall.FILE_ACTION_ADDED:
|
||||
return sysFSCREATE
|
||||
case syscall.FILE_ACTION_REMOVED:
|
||||
return sysFSDELETE
|
||||
case syscall.FILE_ACTION_MODIFIED:
|
||||
return sysFSMODIFY
|
||||
case syscall.FILE_ACTION_RENAMED_OLD_NAME:
|
||||
return sysFSMOVEDFROM
|
||||
case syscall.FILE_ACTION_RENAMED_NEW_NAME:
|
||||
return sysFSMOVEDTO
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user