diff --git a/README.md b/README.md index 3d40677..8755980 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Grapevine + A mobile application that enables anonymous gossip messaging peer-to-peer with devices you physically pass. Messages are ranked by the number of devices they have reached (ie. the virality). This digital approach to gossiping will bring new meaning to “I heard it through the grapevine." -## Technology Overview +## Technology Overview ### API @@ -11,12 +12,18 @@ Our platform-neutral data structure for exchanges from device to device and serv ### App -App specific docs [here](/app). +App specific docs [here](/app). The foundation of the mobile application is react native. Custom platform specific modules are used to perform bluetooth low energy networking. ![app_arch](./assets/app_arch.png) -### Server (v1) +### CLI + +Command-line specific docs [here](/cli). + +Tool for interacting with Grapevine devices and related services. + +### Web -Server specific docs [here](/server). \ No newline at end of file +Simple web interface for interacting with Grapevine devices. diff --git a/cli/.idea/.gitignore b/cli/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/cli/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/cli/.idea/cli.iml b/cli/.idea/cli.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/cli/.idea/cli.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/cli/.idea/modules.xml b/cli/.idea/modules.xml new file mode 100644 index 0000000..6fcc0dc --- /dev/null +++ b/cli/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/cli/.idea/vcs.xml b/cli/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/cli/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..52f34a8 --- /dev/null +++ b/cli/README.md @@ -0,0 +1,28 @@ +# CLI + +Command-line tool for interacting with a Grapevine device and the related services. + +## Installation + +The included build is for MacOS. Executables can be [built for different architectures](https://www.digitalocean.com/community/tutorials/how-to-build-go-executables-for-multiple-platforms-on-ubuntu-16-04) if needed. + +### _Optional_ + +Add the cli directory to your path, so you can reference the executable directly in your command line. + +MacOS +```bash +# Get current path +pwd + +# Add it to your paths file +sudo vim /etc/paths +``` + +## Usage + +``` bash +grapevine --help +``` + +> If you haven't added the cli directory to your path, prepend a `./` to the above command diff --git a/cli/bluetooth/central.go b/cli/bluetooth/central.go new file mode 100644 index 0000000..9907cad --- /dev/null +++ b/cli/bluetooth/central.go @@ -0,0 +1,67 @@ +package bluetooth + +import ( + "grapevine/cli/util" + "tinygo.org/x/bluetooth" +) + +var adapter = bluetooth.DefaultAdapter + +func Scan(scanAll bool) { + initCentral() + println("scanning...") + err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) { + if scanAll || device.HasServiceUUID(getUUID(GrapevineServiceUuid)) { + println("found device:", device.Address.String(), device.RSSI, device.LocalName()) + } + }) + util.Must("start scan", err) +} + +func Connect(targetDevice string) { + initCentral() + grapevineServiceUUID := getUUID(GrapevineServiceUuid) + ch := make(chan bluetooth.ScanResult, 1) + err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) { + if result.Address.String() == targetDevice { + if !result.HasServiceUUID(grapevineServiceUUID) { + panic("grapevine service not found") + } + adapter.StopScan() + ch <- result + } + }) + util.Must("start scan", err) + var device *bluetooth.Device + select { + case result := <-ch: + device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{}) + util.Must("connect to device", err) + println("connected to ", result.Address.String()) + } + + services, err := device.DiscoverServices([]bluetooth.UUID{grapevineServiceUUID}) + util.Must("discover services", err) + if len(services) != 1 { + panic("could not discover grapevine service") + } + characteristics, err := services[0].DiscoverCharacteristics([]bluetooth.UUID{getUUID(MessageCharacteristicUuid)}) + util.Must("discover characteristics", err) + if len(characteristics) != 1 { + panic("could not discover message characteristic") + } + var data []byte + n, err := characteristics[0].Read(data) + util.Must("read characteristic", err) + println(n, string(data)) +} + +func initCentral() { + util.Must("enable BLE stack", adapter.Enable()) +} + +func getUUID(uuidString string) bluetooth.UUID { + uuid, err := bluetooth.ParseUUID(uuidString) + util.Must("parse service uuid", err) + return uuid +} diff --git a/cli/bluetooth/const.go b/cli/bluetooth/const.go new file mode 100644 index 0000000..816ceff --- /dev/null +++ b/cli/bluetooth/const.go @@ -0,0 +1,6 @@ +package bluetooth + +const ( + GrapevineServiceUuid = "acce5bbb-e206-4ac8-9f02-fc9a77f4efc4" + MessageCharacteristicUuid = "3fa13acd-e384-40fc-87a4-2c5fe5eec067" +) diff --git a/cli/build.sh b/cli/build.sh new file mode 100755 index 0000000..a168773 --- /dev/null +++ b/cli/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +go build -o grapevine main.go \ No newline at end of file diff --git a/cli/go.mod b/cli/go.mod new file mode 100644 index 0000000..440bd1a --- /dev/null +++ b/cli/go.mod @@ -0,0 +1,21 @@ +module grapevine/cli + +go 1.17 + +require ( + gopkg.in/alecthomas/kingpin.v2 v2.2.6 + tinygo.org/x/bluetooth v0.4.0 +) + +require ( + github.com/JuulLabs-OSS/cbgo v0.0.2 // indirect + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/go-ole/go-ole v1.2.4 // indirect + github.com/godbus/dbus/v5 v5.0.3 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/muka/go-bluetooth v0.0.0-20210812063148-b6c83362e27d // indirect + github.com/sirupsen/logrus v1.6.0 // indirect + golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 // indirect +) diff --git a/cli/go.sum b/cli/go.sum new file mode 100644 index 0000000..b62a6e2 --- /dev/null +++ b/cli/go.sum @@ -0,0 +1,80 @@ +github.com/JuulLabs-OSS/cbgo v0.0.2 h1:gCDyT0+EPuI8GOFyvAksFcVD2vF4CXBAVwT6uVnD9oo= +github.com/JuulLabs-OSS/cbgo v0.0.2/go.mod h1:L4YtGP+gnyD84w7+jN66ncspFRfOYB5aj9QSXaFHmBA= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= +github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/muka/go-bluetooth v0.0.0-20210812063148-b6c83362e27d h1:EG/xyWjHT19rkUpwsWSkyiCCmyqNwFovr9m10rhyOxU= +github.com/muka/go-bluetooth v0.0.0-20210812063148-b6c83362e27d/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8= +github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +tinygo.org/x/bluetooth v0.4.0 h1:OyeGUksIadi4yXs5T0A2KjMO3/eQWexTFFMcNF2hhFw= +tinygo.org/x/bluetooth v0.4.0/go.mod h1:hTCjyNQWWG443oAeJihZSNnMatTxCVYPmabtIDZUY6c= +tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= +tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI= +tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM= +tinygo.org/x/tinyterm v0.1.0/go.mod h1:/DDhNnGwNF2/tNgHywvyZuCGnbH3ov49Z/6e8LPLRR4= diff --git a/cli/grapevine b/cli/grapevine new file mode 100755 index 0000000..4c2fa27 Binary files /dev/null and b/cli/grapevine differ diff --git a/cli/main.go b/cli/main.go new file mode 100644 index 0000000..7e6d16f --- /dev/null +++ b/cli/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "gopkg.in/alecthomas/kingpin.v2" + "grapevine/cli/bluetooth" + "os" +) + +var ( + app = kingpin.New("grapevine", "Grapevine command-line tool.") + + bluetoothCmd = app.Command("bluetooth", "Perform a bluetooth action.") + + scanCmd = bluetoothCmd.Command("scan", "Scan for Grapevine devices.") + scanAll = scanCmd.Flag("all", "Detect all devices").Short('a').Default("false").Bool() + + connectCmd = bluetoothCmd.Command("connect", "Connect to a Grapevine device.") + device = connectCmd.Arg("device", "Device address you wish to connect to.").Required().String() +) + +func main() { + switch kingpin.MustParse(app.Parse(os.Args[1:])) { + case scanCmd.FullCommand(): + bluetooth.Scan(*scanAll) + case connectCmd.FullCommand(): + bluetooth.Connect(*device) + } +} diff --git a/cli/util/util.go b/cli/util/util.go new file mode 100644 index 0000000..706e89e --- /dev/null +++ b/cli/util/util.go @@ -0,0 +1,7 @@ +package util + +func Must(action string, err error) { + if err != nil { + panic("failed to " + action + ": " + err.Error()) + } +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/.gitignore b/cli/vendor/github.com/JuulLabs-OSS/cbgo/.gitignore new file mode 100644 index 0000000..eaa8df4 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/.gitignore @@ -0,0 +1 @@ +examples/discover/discover diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/LICENSE b/cli/vendor/github.com/JuulLabs-OSS/cbgo/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/README.md b/cli/vendor/github.com/JuulLabs-OSS/cbgo/README.md new file mode 100644 index 0000000..97b117f --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/README.md @@ -0,0 +1,25 @@ +# cbgo + +cbgo implements Go bindings for [CoreBluetooth](https://developer.apple.com/documentation/corebluetooth?language=objc). + +## Documentation + +For documentation, see the [CoreBluetooth docs](https://developer.apple.com/documentation/corebluetooth?language=objc). + +Examples are in the `examples` directory. + +## Scope + +cbgo aims to implement all functionality that is supported in macOS 10.13. + +## Naming + +Function and type names in cbgo are intended to match the corresponding CoreBluetooth functionality as closely as possible. There are a few (consistent) deviations: + +* All cbgo identifiers start with a capital letter to make them public. +* Named arguments in CoreBluetooth functions are eliminated. +* Properties are implemented as a pair of functions (`PropertyName` and `SetPropertyName`). + +## Issues + +There are definitely memory leaks. ARC is not compatible with cgo, so objective C memory has to be managed manually. I didn't see a set of consistent guidelines for object ownership in the CoreBluetooth documentation, so cbgo errs on the side of leaking. Hopefully this is only an issue for very long running processes! Any fixes here are much appreciated. diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/adv.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/adv.go new file mode 100644 index 0000000..360d8f3 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/adv.go @@ -0,0 +1,26 @@ +package cbgo + +type AdvServiceData struct { + UUID UUID + Data []byte +} + +// AdvFields represents the contents of an advertisement received during +// scanning. +type AdvFields struct { + LocalName string + ManufacturerData []byte + TxPowerLevel *int + Connectable *bool + ServiceUUIDs []UUID + ServiceData []AdvServiceData +} + +// AdvData represents the configurable portion of outgoing advertisements. +type AdvData struct { + LocalName string + ServiceUUIDs []UUID + + // If len>0, this overrides the other fields. + IBeaconData []byte +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/att.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/att.go new file mode 100644 index 0000000..689a242 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/att.go @@ -0,0 +1,78 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" +import "unsafe" + +// AttributePermissions: https://developer.apple.com/documentation/corebluetooth/cbattributepermissions +type AttributePermissions int + +const ( + AttributePermissionsReadable = AttributePermissions(C.CBAttributePermissionsReadable) + AttributePermissionsWriteable = AttributePermissions(C.CBAttributePermissionsWriteable) + AttributePermissionsReadEncryptionRequired = AttributePermissions(C.CBAttributePermissionsReadEncryptionRequired) + AttributePermissionsWriteEncryptionRequired = AttributePermissions(C.CBAttributePermissionsWriteEncryptionRequired) +) + +// ATTError: https://developer.apple.com/documentation/corebluetooth/cbatterror +type ATTError int + +const ( + ATTErrorSuccess = ATTError(C.CBATTErrorSuccess) + ATTErrorInvalidHandle = ATTError(C.CBATTErrorInvalidHandle) + ATTErrorReadNotPermitted = ATTError(C.CBATTErrorReadNotPermitted) + ATTErrorWriteNotPermitted = ATTError(C.CBATTErrorWriteNotPermitted) + ATTErrorInvalidPdu = ATTError(C.CBATTErrorInvalidPdu) + ATTErrorInsufficientAuthentication = ATTError(C.CBATTErrorInsufficientAuthentication) + ATTErrorRequestNotSupported = ATTError(C.CBATTErrorRequestNotSupported) + ATTErrorInvalidOffset = ATTError(C.CBATTErrorInvalidOffset) + ATTErrorInsufficientAuthorization = ATTError(C.CBATTErrorInsufficientAuthorization) + ATTErrorPrepareQueueFull = ATTError(C.CBATTErrorPrepareQueueFull) + ATTErrorAttributeNotFound = ATTError(C.CBATTErrorAttributeNotFound) + ATTErrorAttributeNotLong = ATTError(C.CBATTErrorAttributeNotLong) + ATTErrorInsufficientEncryptionKeySize = ATTError(C.CBATTErrorInsufficientEncryptionKeySize) + ATTErrorInvalidAttributeValueLength = ATTError(C.CBATTErrorInvalidAttributeValueLength) + ATTErrorUnlikelyError = ATTError(C.CBATTErrorUnlikelyError) + ATTErrorInsufficientEncryption = ATTError(C.CBATTErrorInsufficientEncryption) + ATTErrorUnsupportedGroupType = ATTError(C.CBATTErrorUnsupportedGroupType) + ATTErrorInsufficientResources = ATTError(C.CBATTErrorInsufficientResources) +) + +// ATTRequest: https://developer.apple.com/documentation/corebluetooth/cbattrequest +type ATTRequest struct { + ptr unsafe.Pointer +} + +// Central: https://developer.apple.com/documentation/corebluetooth/cbattrequest/1518995-central +func (r ATTRequest) Central() Central { + ptr := C.cb_atr_central(r.ptr) + return Central{unsafe.Pointer(ptr)} +} + +// Characteristic: https://developer.apple.com/documentation/corebluetooth/cbattrequest/1518716-characteristic +func (r ATTRequest) Characteristic() Characteristic { + ptr := C.cb_atr_characteristic(r.ptr) + return Characteristic{unsafe.Pointer(ptr)} +} + +// Value: https://developer.apple.com/documentation/corebluetooth/cbattrequest/1518795-value +func (r ATTRequest) Value() []byte { + ba := C.cb_atr_value(r.ptr) + return byteArrToByteSlice(&ba) +} + +// SetValue: https://developer.apple.com/documentation/corebluetooth/cbattrequest/1518795-value +func (r ATTRequest) SetValue(v []byte) { + ba := byteSliceToByteArr(v) + defer C.free(unsafe.Pointer(ba.data)) + + C.cb_atr_set_value(r.ptr, &ba) +} + +// Offset: https://developer.apple.com/documentation/corebluetooth/cbattrequest/1518857-offset +func (r ATTRequest) Offset() int { + return int(C.cb_atr_offset(r.ptr)) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.go new file mode 100644 index 0000000..1e6239b --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.go @@ -0,0 +1,28 @@ +package cbgo + +import ( + "os" + + "github.com/sirupsen/logrus" +) + +var btlog = &logrus.Logger{ + Out: os.Stderr, + Formatter: &logrus.TextFormatter{ + FullTimestamp: true, + TimestampFormat: "2006-01-02 15:04:05.999", + }, + Level: logrus.ErrorLevel, +} + +// SetLog replaces the cbgo logger with a custom one. +func SetLog(log *logrus.Logger) { + btlog = log +} + +// SetLogLevel configures the cbgo logger to use the specified log level. +func SetLogLevel(level logrus.Level) { + if btlog != nil { + btlog.SetLevel(level) + } +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.h b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.h new file mode 100644 index 0000000..d810727 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.h @@ -0,0 +1,220 @@ +#ifndef H_BT_ +#define H_BT_ + +#import +#import + +#define ADV_FIELDS_PWR_LVL_NONE (-128) +#define ADV_FIELDS_CONNECTABLE_NONE (-1) + +struct byte_arr { + const uint8_t *data; + int length; +}; + +struct string_arr { + const char **strings; + int count; +}; + +struct obj_arr { + void **objs; + int count; +}; + +struct bt_error { + const char *msg; + int code; +}; + +struct adv_fields { + const char *name; + struct byte_arr mfg_data; + struct string_arr svc_uuids; + struct string_arr overflow_svc_uuids; + int8_t pwr_lvl; // -128 = not present + int connectable; // -1 = not present + struct string_arr svc_data_uuids; + struct byte_arr *svc_data_values; +}; + +struct adv_data { + const char *name; + struct string_arr svc_uuids; + struct byte_arr ibeacon_data; +}; + +struct scan_opts { + bool allow_dups; + struct string_arr sol_svc_uuids; +}; + +struct connect_opts { + bool notify_on_connection; + bool notify_on_disconnection; + bool notify_on_notification; + bool enable_transport_bridging; + bool requires_ancs; + int start_delay; +}; + +struct cmgr_restore_opts { + struct obj_arr prphs; + struct string_arr scan_svcs; + struct scan_opts *scan_opts; +}; + +struct pmgr_restore_opts { + struct obj_arr svcs; + struct adv_data *adv_data; +}; + +@interface BTDlg : NSObject +{ +} +@end + +// bt.m +bool bt_start(); +void bt_stop(); +void bt_init(); + +// util.m +struct byte_arr nsdata_to_byte_arr(const NSData *nsdata); +NSData *byte_arr_to_nsdata(const struct byte_arr *ba); +struct obj_arr nsarray_to_obj_arr(const NSArray *arr); +NSArray *obj_arr_to_nsarray(const struct obj_arr *oa); +NSString *str_to_nsstring(const char *s); +struct bt_error nserror_to_bt_error(const NSError *err); +struct string_arr cbuuids_to_strs(const NSArray *cbuuids); +int dict_int(NSDictionary *dict, NSString *key, int dflt); +const char *dict_string(NSDictionary *dict, NSString *key); +const void *dict_data(NSDictionary *dict, NSString *key, int *out_len); +const struct byte_arr dict_bytes(NSDictionary *dict, NSString *key); +void dict_set_bool(NSMutableDictionary *dict, NSString *key, bool val); +void dict_set_int(NSMutableDictionary *dict, NSString *key, int val); +NSUUID *str_to_nsuuid(const char *s); +CBUUID *str_to_cbuuid(const char *s); +NSArray *strs_to_nsuuids(const struct string_arr *sa); +NSArray *strs_to_cbuuids(const struct string_arr *sa); +NSArray *strs_to_nsstrings(const struct string_arr *sa); + +// cmgr.m +CBCentralManager *cb_alloc_cmgr(bool pwr_alert, const char *restore_id); +void cb_cmgr_set_delegate(void *cmgr, bool set); +int cb_cmgr_state(void *cm); +void cb_cmgr_scan(void *cmgr, const struct string_arr *svc_uuids, + const struct scan_opts *opts); +void cb_cmgr_stop_scan(void *cm); +bool cb_cmgr_is_scanning(void *cm); +void cb_cmgr_connect(void *cmgr, void *prph, const struct connect_opts *opts); +void cb_cmgr_cancel_connect(void *cmgr, void *prph); +struct obj_arr cb_cmgr_retrieve_prphs_with_svcs(void *cmgr, const struct string_arr *svc_uuids); +struct obj_arr cb_cmgr_retrieve_prphs(void *cmgr, const struct string_arr *uuids); + +const char *cb_peer_identifier(void *prph); + +void cb_prph_set_delegate(void *prph, bool set); +const char *cb_prph_name(void *prph); +struct obj_arr cb_prph_services(void *prph); +void cb_prph_discover_svcs(void *prph, const struct string_arr *svc_uuid_strs); +void cb_prph_discover_included_svcs(void *prph, const struct string_arr *svc_uuid_strs, void *svc); +void cb_prph_discover_chrs(void *prph, void *svc, const struct string_arr *chr_uuid_strs); +void cb_prph_discover_dscs(void *prph, void *chr); +void cb_prph_read_chr(void *prph, void *chr); +void cb_prph_read_dsc(void *prph, void *dsc); +void cb_prph_write_chr(void *prph, void *chr, struct byte_arr *value, int type); +void cb_prph_write_dsc(void *prph, void *dsc, struct byte_arr *value); +int cb_prph_max_write_len(void *prph, int type); +void cb_prph_set_notify(void *prph, bool enabled, void *chr); +int cb_prph_state(void *prph); +bool cb_prph_can_send_write_without_rsp(void *prph); +void cb_prph_read_rssi(void *prph); +bool cb_prph_ancs_authorized(void *prph); + +const char *cb_svc_uuid(void *svc); +void *cb_svc_peripheral(void *svc); +bool cb_svc_is_primary(void *svc); +struct obj_arr cb_svc_characteristics(void *svc); +struct obj_arr cb_svc_included_svcs(void *svc); + +const char *cb_chr_uuid(void *chr); +void *cb_chr_service(void *chr); +struct obj_arr cb_chr_descriptors(void *chr); +struct byte_arr cb_chr_value(void *chr); +int cb_chr_properties(void *chr); +bool cb_chr_is_notifying(void *chr); + +const char *cb_dsc_uuid(void *dsc); +void *cb_dsc_characteristic(void *dsc); +struct byte_arr cb_dsc_value(void *dsc); + +// pmgr.m +CBPeripheralManager *cb_alloc_pmgr(bool pwr_alert, const char *restore_id); +void cb_pmgr_set_delegate(void *pmgr, bool set); +int cb_pmgr_state(void *pmgr); +void cb_pmgr_add_svc(void *pmgr, void *svc); +void cb_pmgr_remove_svc(void *pmgr, void *svc); +void cb_pmgr_remove_all_svcs(void *pmgr); +void cb_pmgr_start_adv(void *pmgr, const struct adv_data *ad); +void cb_pmgr_stop_adv(void *pmgr); +bool cb_pmgr_is_adv(void *pmgr); +bool cb_pmgr_update_val(void *pmgr, const struct byte_arr *value, void *chr, const struct obj_arr *centrals); +void cb_pmgr_respond_to_req(void *pmgr, void *req, int result); +void cb_pmgr_set_conn_latency(void *pmgr, int latency, void *central); + +int cb_cent_maximum_update_len(void *cent); + +CBMutableService *cb_msvc_alloc(const char *uuid, bool primary); +void cb_msvc_set_characteristics(void *msvc, const struct obj_arr *mchrs); +void cb_msvc_set_included_services(void *msvc, const struct obj_arr *msvcs); + +CBMutableCharacteristic *cb_mchr_alloc(const char *uuid, int properties, const struct byte_arr *value, + int permissions); +void cb_mchr_set_value(void *mchr, const struct byte_arr *val); +void cb_mchr_set_descriptors(void *mchr, const struct obj_arr *mdscs); + +CBMutableDescriptor *cb_mdsc_alloc(const char *uuid, const struct byte_arr *value); + +CBCentral *cb_atr_central(void *atr); +CBCharacteristic *cb_atr_characteristic(void *atr); +struct byte_arr cb_atr_value(void *atr); +void cb_atr_set_value(void *atr, const struct byte_arr *ba); +int cb_atr_offset(void *atr); + +// cbhandlers.go +void BTCentralManagerDidConnectPeripheral(void *cmgr, void *prph); +void BTCentralManagerDidFailToConnectPeripheral(void *cmgr, void *prph, struct bt_error *err); +void BTCentralManagerDidDisconnectPeripheral(void *cmgr, void *prph, struct bt_error *err); +void BTCentralManagerConnectionEventDidOccur(void *cmgr, int event, void *prph); +void BTCentralManagerDidDiscoverPeripheral(void *cmgr, void *prph, struct adv_fields *advData, int rssi); +void BTCentralManagerDidUpdateState(void *cmgr); +void BTCentralManagerWillRestoreState(void *cmgr, struct cmgr_restore_opts *opts); +void BTPeripheralDidDiscoverServices(void *prph, struct bt_error *err); +void BTPeripheralDidDiscoverIncludedServices(void *prph, void *svc, struct bt_error *err); +void BTPeripheralDidDiscoverCharacteristics(void *prph, void *svc, struct bt_error *err); +void BTPeripheralDidDiscoverDescriptors(void *prph, void *chr, struct bt_error *err); +void BTPeripheralDidUpdateValueForCharacteristic(void *prph, void *chr, struct bt_error *err); +void BTPeripheralDidUpdateValueForDescriptor(void *prph, void *dsc, struct bt_error *err); +void BTPeripheralDidWriteValueForCharacteristic(void *prph, void *chr, struct bt_error *err); +void BTPeripheralDidWriteValueForDescriptor(void *prph, void *dsc, struct bt_error *err); +void BTPeripheralIsReadyToSendWriteWithoutResponse(void *prph); +void BTPeripheralDidUpdateNotificationState(void *prph, void *chr, struct bt_error *err); +void BTPeripheralDidReadRSSI(void *prph, int rssi, struct bt_error *err); +void BTPeripheralDidUpdateName(void *prph); +void BTPeripheralDidModifyServices(void *prph, struct obj_arr *inv_svcs); + +void BTPeripheralManagerDidUpdateState(void *pmgr); +void BTPeripheralManagerWillRestoreState(void *pmgr, struct pmgr_restore_opts *opts); +void BTPeripheralManagerDidAddService(void *pmgr, void *svc, struct bt_error *err); +void BTPeripheralManagerDidStartAdvertising(void *pmgr, struct bt_error *err); +void BTPeripheralManagerCentralDidSubscribe(void *pmgr, void *cent, void *chr); +void BTPeripheralManagerCentralDidUnsubscribe(void *pmgr, void *cent, void *chr); +void BTPeripheralManagerIsReadyToUpdateSubscribers(void *pmgr); +void BTPeripheralManagerDidReceiveReadRequest(void *pmgr, void *req); +void BTPeripheralManagerDidReceiveWriteRequests(void *pmgr, struct obj_arr *oa); + +extern dispatch_queue_t bt_queue; +extern BTDlg *bt_dlg; + +#endif diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.m b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.m new file mode 100644 index 0000000..1de9797 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/bt.m @@ -0,0 +1,60 @@ +#import +#import +#import "bt.h" + +dispatch_queue_t bt_queue; +static bool bt_loop_active; + +/** + * Universal delegate. All callbacks get funnelled through this object before + * being translated to the appropriate Go calls. + */ +BTDlg *bt_dlg; + +/** + * Starts a thread that processes CoreBluetooth events from the BT queue. + * + * @return false if the thread was already started; + * true if this call started a new thread. + */ +bool +bt_start() +{ + if (bt_loop_active) { + return false; + } + bt_loop_active = true; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSRunLoop *rl; + rl = [NSRunLoop currentRunLoop]; + + bool done; + do { + done = [rl runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } while (bt_loop_active && !done); + }); + + return true; +} + +void +bt_stop() +{ + bt_loop_active = false; +} + +void +bt_init() +{ + // XXX: I have no idea why a separate queue is required here. When I + // attempt to use the default queue, the run loop does not receive any + // events. + if (bt_queue == NULL) { + bt_queue = dispatch_queue_create("bt_queue", NULL); + } + + bt_dlg = [[BTDlg alloc] init]; + [bt_dlg retain]; +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/btdlg.m b/cli/vendor/github.com/JuulLabs-OSS/cbgo/btdlg.m new file mode 100644 index 0000000..d565792 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/btdlg.m @@ -0,0 +1,444 @@ +#import +#import +#import "bt.h" + +@implementation BTDlg + +- (id) +init +{ + self = [super init]; + if (self == nil) { + return nil; + } + + return self; +} + +/** + * Called when the central manager successfully connects to a peripheral. + */ +- (void) + centralManager:(CBCentralManager *)cm +didConnectPeripheral:(CBPeripheral *)prph +{ + prph.delegate = self; + BTCentralManagerDidConnectPeripheral(cm, prph); +} + +/** + * Called when a connection to a preipheral is terminated. + */ +- (void) + centralManager:(CBCentralManager *)cm +didDisconnectPeripheral:(CBPeripheral *)prph + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTCentralManagerDidDisconnectPeripheral(cm, prph, &err); +} + +/** + * Called when the central manager fails to connect to a peripheral. + */ +- (void) + centralManager:(CBCentralManager *)cm +didFailToConnectPeripheral:(CBPeripheral *)prph + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTCentralManagerDidFailToConnectPeripheral(cm, prph, &err); +} + +// macOS 10.15+ +#if 0 +- (void) + centralManager:(CBCentralManager *)cm +connectionEventDidOccur:(CBConnectionEvent)event + forPeripheral:(CBPeripheral *)prph +{ + BTCentralManagerConnectionEventDidOccur(cm, event, prph); +} +#endif + +/** + * Called when the central manager discovers a peripheral while scanning for + * devices. + */ +- (void) + centralManager:(CBCentralManager *)cm +didDiscoverPeripheral:(CBPeripheral *)prph + advertisementData:(NSDictionary *)advData + RSSI:(NSNumber *)RSSI +{ + struct adv_fields af = {0}; + + af.name = dict_string(advData, CBAdvertisementDataLocalNameKey); + af.mfg_data = dict_bytes(advData, CBAdvertisementDataManufacturerDataKey); + af.pwr_lvl = dict_int(advData, CBAdvertisementDataTxPowerLevelKey, ADV_FIELDS_PWR_LVL_NONE); + af.connectable = dict_int(advData, CBAdvertisementDataIsConnectable, ADV_FIELDS_CONNECTABLE_NONE); + + const NSArray *arr = [advData objectForKey:CBAdvertisementDataServiceUUIDsKey]; + const char *svc_uuids[[arr count]]; + for (int i = 0; i < [arr count]; i++) { + const CBUUID *uuid = [arr objectAtIndex:i]; + svc_uuids[i] = [[uuid UUIDString] UTF8String]; + } + af.svc_uuids = (struct string_arr) { + .strings = svc_uuids, + .count = [arr count], + }; + + const NSDictionary *dict = [advData objectForKey:CBAdvertisementDataServiceDataKey]; + const NSArray *keys = [dict allKeys]; + + const char *svc_data_uuids[[keys count]]; + struct byte_arr svc_data_values[[keys count]]; + + for (int i = 0; i < [keys count]; i++) { + const CBUUID *uuid = [keys objectAtIndex:i]; + svc_data_uuids[i] = [[uuid UUIDString] UTF8String]; + + const NSData *data = [dict objectForKey:uuid]; + svc_data_values[i].data = [data bytes]; + svc_data_values[i].length = [data length]; + } + af.svc_data_uuids = (struct string_arr) { + .strings = svc_data_uuids, + .count = [keys count], + }; + af.svc_data_values = svc_data_values; + + prph.delegate = self; + [prph retain]; + + BTCentralManagerDidDiscoverPeripheral(cm, prph, &af, [RSSI intValue]); +} + +/** + * Called whenever the central manager's state is updated. + */ +- (void) +centralManagerDidUpdateState:(CBCentralManager *)cm +{ + BTCentralManagerDidUpdateState(cm); +} + +/** + * Called when the central manager is about to restore its state (as requested + * by the application). + */ +- (void) + centralManager:(CBCentralManager *)cm +willRestoreState:(NSDictionary *)dict +{ + struct cmgr_restore_opts opts = {0}; + + const NSArray *prphs = [dict objectForKey:CBCentralManagerRestoredStatePeripheralsKey]; + opts.prphs = nsarray_to_obj_arr(prphs); + + const NSArray *uuids = [dict objectForKey:CBCentralManagerRestoredStateScanServicesKey]; + opts.scan_svcs = cbuuids_to_strs(uuids); + + struct scan_opts scan_opts = {0}; + const NSDictionary *scan_dict = [dict objectForKey:CBCentralManagerRestoredStateScanOptionsKey]; + if (scan_dict != nil && [scan_dict count] > 0) { + opts.scan_opts = &scan_opts; + + NSNumber *dups = [scan_dict objectForKey:CBCentralManagerScanOptionAllowDuplicatesKey]; + if (dups != nil && [dups boolValue]) { + opts.scan_opts->allow_dups = true; + } + + NSArray *sol_uuids = [scan_dict objectForKey:CBCentralManagerScanOptionSolicitedServiceUUIDsKey]; + opts.scan_opts->sol_svc_uuids = cbuuids_to_strs(sol_uuids); + } + + BTCentralManagerWillRestoreState(cm, &opts); + + free(scan_opts.sol_svc_uuids.strings); + free(opts.scan_svcs.strings); + free(opts.prphs.objs); +} + +/** + * Called when the central manager successfully discovers services on a + * peripheral. + */ +- (void) peripheral:(CBPeripheral *)prph +didDiscoverServices:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidDiscoverServices(prph, &err); +} + +/** + * Called when the central manager successfully discovers included services of + * a peripheral's primary service. + */ +- (void) peripheral:(CBPeripheral *)prph +didDiscoverIncludedServicesForService:(CBService *)svc + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidDiscoverIncludedServices(prph, svc, &err); +} + +/** + * Called when the central manager successfully discovers characteristics of a + * peripheral's service. + */ +- (void) + peripheral:(CBPeripheral *)prph +didDiscoverCharacteristicsForService:(CBService *)svc + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidDiscoverCharacteristics(prph, svc, &err); +} + +/** + * Called when the central manager successfully discovers descriptors of a + * peripheral's characteristic. + */ +- (void) + peripheral:(CBPeripheral *)prph +didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)chr + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidDiscoverDescriptors(prph, chr, &err); +} + +/** + * Called when a connected peripheral communicates a characteristic's value + * (via read response, notification, or indication). + */ +- (void) + peripheral:(CBPeripheral *)prph +didUpdateValueForCharacteristic:(CBCharacteristic *)chr + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidUpdateValueForCharacteristic(prph, chr, &err); +} + +/** + * Called when a connected peripheral communicates a descriptors's value + * (via read response). + */ +- (void) + peripheral:(CBPeripheral *)prph +didUpdateValueForDescriptor:(CBDescriptor *)dsc + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidUpdateValueForDescriptor(prph, dsc, &err); +} + +/** + * Called when a connected peripheral responds to a Characteristic Write + * Request. + */ +- (void) + peripheral:(CBPeripheral *)prph +didWriteValueForCharacteristic:(CBCharacteristic *)chr + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidWriteValueForCharacteristic(prph, chr, &err); +} + +/** + * Called when a connected peripheral responds to a Descriptor Write Request. + */ +- (void) + peripheral:(CBPeripheral *)prph +didWriteValueForDescriptor:(CBDescriptor *)dsc + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidWriteValueForDescriptor(prph, dsc, &err); +} + +/** + * Called when a previously unsuccessful Write Without Response request can be + * retried. + */ +- (void) +peripheralIsReadyToSendWriteWithoutResponse:(CBPeripheral *)prph +{ + BTPeripheralIsReadyToSendWriteWithoutResponse(prph); +} + +/** + * Called when we (un)subscribe to a connected peripheral's characteristic. + */ +- (void) + peripheral:(CBPeripheral *)prph +didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)chr + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidUpdateNotificationState(prph, chr, &err); +} + +/** + * Called when we read a connected peripheral's RSSI. + */ +- (void) + peripheral:(CBPeripheral *)prph +didReadRSSI:(NSNumber *)RSSI + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralDidReadRSSI(prph, [RSSI intValue], &err); +} + +/** + * Called when a connected peripheral changes its name. + */ +- (void) +peripheralDidUpdateName:(CBPeripheral *)prph +{ + BTPeripheralDidUpdateName(prph); +} + +/** + * Called when a connected peripheral changes its set of services. + */ +- (void) + peripheral:(CBPeripheral *)prph +didModifyServices:(NSArray *)invSvcs +{ + struct obj_arr oa = nsarray_to_obj_arr(invSvcs); + BTPeripheralDidModifyServices(prph, &oa); + free(oa.objs); +} + + +/** + * Called whenever the peripheral manager's state is updated. + */ +- (void) +peripheralManagerDidUpdateState:(CBPeripheralManager *)pm +{ + BTPeripheralManagerDidUpdateState(pm); +} + +/** + * Called when the peripheral manager is about to restore its state (as + * requested by the application). + */ +- (void) +peripheralManager:(CBPeripheralManager *)pmgr + willRestoreState:(NSDictionary *)dict +{ + struct pmgr_restore_opts opts = {0}; + + const NSArray *svcs = [dict objectForKey:CBPeripheralManagerRestoredStateServicesKey]; + opts.svcs = nsarray_to_obj_arr(svcs); + + struct adv_data adv_data = {0}; + const NSDictionary *ad_dict = [dict objectForKey:CBPeripheralManagerRestoredStateAdvertisementDataKey]; + if (ad_dict != nil && [dict count] > 0) { + opts.adv_data = &adv_data; + + NSString *nss = [ad_dict objectForKey:CBAdvertisementDataLocalNameKey]; + if (nss != nil) { + adv_data.name = [nss UTF8String]; + } + + NSArray *nsa = [ad_dict objectForKey:CBAdvertisementDataServiceUUIDsKey]; + if (nsa != nil) { + adv_data.svc_uuids = cbuuids_to_strs(nsa); + } + } + + BTPeripheralManagerWillRestoreState(pmgr, &opts); + + free(adv_data.svc_uuids.strings); + free(opts.svcs.objs); +} + +/** + * Called when an attempt to register a service has completed. + */ +- (void) +peripheralManager:(CBPeripheralManager *)pmgr + didAddService:(CBService *)svc + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralManagerDidAddService(pmgr, svc, &err); +} + +/** + * Called when we start advertising or fail to advertise. + */ +- (void) +peripheralManagerDidStartAdvertising:(CBPeripheralManager *)pmgr + error:(NSError *)nserr +{ + struct bt_error err = nserror_to_bt_error(nserr); + BTPeripheralManagerDidStartAdvertising(pmgr, &err); +} + +/** + * Called when a connected central has subscribed to one of our + * characteristics. + */ +- (void) + peripheralManager:(CBPeripheralManager *)pmgr + central:(CBCentral *)cent +didSubscribeToCharacteristic:(CBCharacteristic *)chr +{ + BTPeripheralManagerCentralDidSubscribe(pmgr, cent, chr); +} + +/** + * Called when a connected central has unsubscribed from one of our + * characteristics. + */ +- (void) + peripheralManager:(CBPeripheralManager *)pmgr + central:(CBCentral *)cent +didUnsubscribeFromCharacteristic:(CBCharacteristic *)chr +{ + BTPeripheralManagerCentralDidUnsubscribe(pmgr, cent, chr); +} + +/** + * Called when a previous unsuccessful attempt to send notifications can be + * retried. + */ +- (void) +peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)pmgr +{ + BTPeripheralManagerIsReadyToUpdateSubscribers(pmgr); +} + +/** + * Called when a connected cerntral has sent us a read request. + */ +- (void) + peripheralManager:(CBPeripheralManager *)pmgr +didReceiveReadRequest:(CBATTRequest *)req +{ + BTPeripheralManagerDidReceiveReadRequest(pmgr, req); +} + +/** + * Called when connected cerntrals have sent us write requests. + */ +- (void) + peripheralManager:(CBPeripheralManager *)pmgr +didReceiveWriteRequests:(NSArray *)reqs +{ + struct obj_arr oa = nsarray_to_obj_arr(reqs); + + BTPeripheralManagerDidReceiveWriteRequests(pmgr, &oa); + free(oa.objs); +} + +@end diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/cbhandlers.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cbhandlers.go new file mode 100644 index 0000000..4944f43 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cbhandlers.go @@ -0,0 +1,406 @@ +package cbgo + +// CBHandlers: Go handlers for asynchronous CoreBluetooth callbacks. + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" +import ( + "unsafe" +) + +//export BTCentralManagerDidConnectPeripheral +func BTCentralManagerDidConnectPeripheral(cmgr unsafe.Pointer, prph unsafe.Pointer) { + btlog.Debugf("CentralManagerDidConnectPeripheral: cmgr=%v prph=%v", cmgr, prph) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.DidConnectPeripheral(CentralManager{cmgr}, Peripheral{prph}) + } +} + +//export BTCentralManagerDidFailToConnectPeripheral +func BTCentralManagerDidFailToConnectPeripheral(cmgr unsafe.Pointer, prph unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("CentralManagerDidFailToConnectPeripheral: cmgr=%v prph=%v err=%v", cmgr, prph, nserr) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.DidFailToConnectPeripheral(CentralManager{cmgr}, Peripheral{prph}, nserr) + } +} + +//export BTCentralManagerDidDisconnectPeripheral +func BTCentralManagerDidDisconnectPeripheral(cmgr unsafe.Pointer, prph unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("CentralManagerDidDisconnectPeripheral: cmgr=%v prph=%v err=%v", cmgr, prph, nserr) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.DidDisconnectPeripheral(CentralManager{cmgr}, Peripheral{prph}, nserr) + } +} + +// macOS 10.15+ +/* +//export BTCentralManagerConnectionEventDidOccur +func BTCentralManagerConnectionEventDidOccur(cmgr unsafe.Pointer, event C.int, prph unsafe.Pointer) { + dlg := findCentralManagerDlg(cmgr) + if dlg == nil { + return + } + + dlg.ConnectionEventDidOccur(CentralManager{cmgr}, ConnectionEvent(event), Peripheral{prph}) +} +*/ + +//export BTCentralManagerDidDiscoverPeripheral +func BTCentralManagerDidDiscoverPeripheral(cmgr unsafe.Pointer, prph unsafe.Pointer, advData *C.struct_adv_fields, + rssi C.int) { + + af := AdvFields{} + af.LocalName = C.GoString(advData.name) + af.ManufacturerData = byteArrToByteSlice(&advData.mfg_data) + if advData.pwr_lvl != C.ADV_FIELDS_PWR_LVL_NONE { + i := int(advData.pwr_lvl) + af.TxPowerLevel = &i + } + if advData.connectable != C.ADV_FIELDS_CONNECTABLE_NONE { + b := advData.connectable != 0 + af.Connectable = &b + } + + af.ServiceUUIDs = mustStrArrToUUIDs(&advData.svc_uuids) + svcDataUUIDs := mustStrArrToUUIDs(&advData.svc_data_uuids) + + for i, u := range svcDataUUIDs { + elemPtr := getArrElemAddr(unsafe.Pointer(advData.svc_data_values), unsafe.Sizeof(*advData.svc_data_values), i) + elem := (*C.struct_byte_arr)(elemPtr) + af.ServiceData = append(af.ServiceData, AdvServiceData{ + UUID: u, + Data: byteArrToByteSlice(elem), + }) + + } + + btlog.Debugf("CentralManagerDidDiscoverPeripheral: cmgr=%v prph=%v af=%+v rssi=%v", cmgr, prph, af, rssi) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.DidDiscoverPeripheral(CentralManager{cmgr}, Peripheral{prph}, af, int(rssi)) + } +} + +//export BTCentralManagerDidUpdateState +func BTCentralManagerDidUpdateState(cmgr unsafe.Pointer) { + btlog.Debugf("CentralManagerDidUpdateState: cmgr=%v", cmgr) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.CentralManagerDidUpdateState(CentralManager{cmgr}) + } +} + +//export BTCentralManagerWillRestoreState +func BTCentralManagerWillRestoreState(cmgr unsafe.Pointer, opts *C.struct_cmgr_restore_opts) { + ropts := CentralManagerRestoreOpts{} + + ropts.Peripherals = make([]Peripheral, opts.prphs.count) + for i, _ := range ropts.Peripherals { + ropts.Peripherals[i].ptr = getObjArrElem(&opts.prphs, i) + } + + ropts.ScanServices = mustStrArrToUUIDs(&opts.scan_svcs) + + if opts.scan_opts != nil { + ropts.CentralManagerScanOpts = &CentralManagerScanOpts{ + AllowDuplicates: bool(opts.scan_opts.allow_dups), + SolicitedServiceUUIDs: mustStrArrToUUIDs(&opts.scan_opts.sol_svc_uuids), + } + } + + btlog.Debugf("CentralManagerWillRestoreState: cmgr=%v opts=%+v", cmgr, ropts) + + dlg := findCentralManagerDlg(cmgr) + if dlg != nil { + dlg.CentralManagerWillRestoreState(CentralManager{cmgr}, ropts) + } +} + +//export BTPeripheralDidDiscoverServices +func BTPeripheralDidDiscoverServices(prph unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidDiscoverServices: prph=%v err=%v", prph, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidDiscoverServices(Peripheral{prph}, nserr) + } +} + +//export BTPeripheralDidDiscoverIncludedServices +func BTPeripheralDidDiscoverIncludedServices(prph unsafe.Pointer, svc unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidDiscoverIncludedServices: prph=%v svc=%v err=%v", prph, svc, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidDiscoverIncludedServices(Peripheral{prph}, Service{svc}, nserr) + } +} + +//export BTPeripheralDidDiscoverCharacteristics +func BTPeripheralDidDiscoverCharacteristics(prph unsafe.Pointer, svc unsafe.Pointer, err *C.struct_bt_error) { + + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidDiscoverCharacteristics: prph=%v svc=%v err=%v", prph, svc, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidDiscoverCharacteristics(Peripheral{prph}, Service{svc}, nserr) + } +} + +//export BTPeripheralDidDiscoverDescriptors +func BTPeripheralDidDiscoverDescriptors(prph unsafe.Pointer, chr unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidDiscoverDescriptors: prph=%v chr=%v err=%v", prph, chr, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidDiscoverDescriptors(Peripheral{prph}, Characteristic{chr}, nserr) + } +} + +//export BTPeripheralDidUpdateValueForCharacteristic +func BTPeripheralDidUpdateValueForCharacteristic(prph unsafe.Pointer, chr unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidUpdateValueForCharacteristic: prph=%v chr=%v err=%v", prph, chr, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidUpdateValueForCharacteristic(Peripheral{prph}, Characteristic{chr}, nserr) + } +} + +//export BTPeripheralDidUpdateValueForDescriptor +func BTPeripheralDidUpdateValueForDescriptor(prph unsafe.Pointer, dsc unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidUpdateValueForDescriptor: prph=%v dsc=%v err=%v", prph, dsc, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidUpdateValueForDescriptor(Peripheral{prph}, Descriptor{dsc}, nserr) + } +} + +//export BTPeripheralDidWriteValueForCharacteristic +func BTPeripheralDidWriteValueForCharacteristic(prph unsafe.Pointer, chr unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidWriteValueForCharacteristic: prph=%v chr=%v err=%v", prph, chr, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidWriteValueForCharacteristic(Peripheral{prph}, Characteristic{chr}, nserr) + } +} + +//export BTPeripheralDidWriteValueForDescriptor +func BTPeripheralDidWriteValueForDescriptor(prph unsafe.Pointer, dsc unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidWriteValueForDescriptor: prph=%v dsc=%v err=%v", prph, dsc, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidWriteValueForDescriptor(Peripheral{prph}, Descriptor{dsc}, nserr) + } +} + +//export BTPeripheralIsReadyToSendWriteWithoutResponse +func BTPeripheralIsReadyToSendWriteWithoutResponse(prph unsafe.Pointer) { + btlog.Debugf("PeripheralIsReadyToSendWriteWithoutResponse: prph=%v", prph) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.IsReadyToSendWriteWithoutResponse(Peripheral{prph}) + } +} + +//export BTPeripheralDidUpdateNotificationState +func BTPeripheralDidUpdateNotificationState(prph unsafe.Pointer, chr unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidUpdateNotificationState: prph=%v chr=%v err=%v", prph, chr, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidUpdateNotificationState(Peripheral{prph}, Characteristic{chr}, nserr) + } +} + +//export BTPeripheralDidReadRSSI +func BTPeripheralDidReadRSSI(prph unsafe.Pointer, rssi C.int, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralDidReadRSSI: prph=%v rssi=%v err=%v", prph, rssi, nserr) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidReadRSSI(Peripheral{prph}, int(rssi), nserr) + } +} + +//export BTPeripheralDidUpdateName +func BTPeripheralDidUpdateName(prph unsafe.Pointer) { + btlog.Debugf("PeripheralDidUpdateName: prph=%v", prph) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidUpdateName(Peripheral{prph}) + } +} + +//export BTPeripheralDidModifyServices +func BTPeripheralDidModifyServices(prph unsafe.Pointer, inv_svcs *C.struct_obj_arr) { + svcs := make([]Service, inv_svcs.count) + for i, _ := range svcs { + elem := getObjArrElem(inv_svcs, i) + svc := Service{elem} + svcs = append(svcs, svc) + } + + btlog.Debugf("PeripheralDidModifyServices: prph=%v inv_svcs=%+v", prph, svcs) + + dlg := findPeripheralDlg(prph) + if dlg != nil { + dlg.DidModifyServices(Peripheral{prph}, svcs) + } +} + +//export BTPeripheralManagerDidUpdateState +func BTPeripheralManagerDidUpdateState(pmgr unsafe.Pointer) { + btlog.Debugf("PeripheralManagerDidUpdateState: pmgr=%v", pmgr) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.PeripheralManagerDidUpdateState(PeripheralManager{pmgr}) + } +} + +//export BTPeripheralManagerWillRestoreState +func BTPeripheralManagerWillRestoreState(pmgr unsafe.Pointer, opts *C.struct_pmgr_restore_opts) { + ropts := PeripheralManagerRestoreOpts{} + + if opts.svcs.count > 0 { + ropts.Services = make([]MutableService, opts.svcs.count) + for i, _ := range ropts.Services { + ropts.Services[i] = MutableService{getObjArrElem(&opts.svcs, i)} + } + } + + if opts.adv_data != nil { + ropts.AdvertisementData = &AdvData{ + LocalName: C.GoString(opts.adv_data.name), + ServiceUUIDs: mustStrArrToUUIDs(&opts.adv_data.svc_uuids), + } + } + + btlog.Debugf("PeripheralManagerWillRestoreState: pmgr=%v opts=%+v", pmgr, ropts) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.PeripheralManagerWillRestoreState(PeripheralManager{pmgr}, ropts) + } +} + +//export BTPeripheralManagerDidAddService +func BTPeripheralManagerDidAddService(pmgr unsafe.Pointer, svc unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralManagerDidAddService: pmgr=%v err=%v", pmgr, nserr) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.DidAddService(PeripheralManager{pmgr}, Service{svc}, nserr) + } +} + +//export BTPeripheralManagerDidStartAdvertising +func BTPeripheralManagerDidStartAdvertising(pmgr unsafe.Pointer, err *C.struct_bt_error) { + nserr := btErrorToNSError(err) + + btlog.Debugf("PeripheralManagerDidStartAdvertising: pmgr=%v err=%+v", pmgr, nserr) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.DidStartAdvertising(PeripheralManager{pmgr}, nserr) + } +} + +//export BTPeripheralManagerCentralDidSubscribe +func BTPeripheralManagerCentralDidSubscribe(pmgr unsafe.Pointer, cent unsafe.Pointer, chr unsafe.Pointer) { + btlog.Debugf("PeripheralManagerCentralDidSubscribe: pmgr=%v cent=%v chr=%v", pmgr, cent, chr) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.CentralDidSubscribe(PeripheralManager{pmgr}, Central{cent}, Characteristic{chr}) + } +} + +//export BTPeripheralManagerCentralDidUnsubscribe +func BTPeripheralManagerCentralDidUnsubscribe(pmgr unsafe.Pointer, cent unsafe.Pointer, chr unsafe.Pointer) { + btlog.Debugf("PeripheralManagerCentralDidUnsubscribe: pmgr=%v cent=%v chr=%v", pmgr, cent, chr) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.CentralDidUnsubscribe(PeripheralManager{pmgr}, Central{cent}, Characteristic{chr}) + } +} + +//export BTPeripheralManagerIsReadyToUpdateSubscribers +func BTPeripheralManagerIsReadyToUpdateSubscribers(pmgr unsafe.Pointer) { + btlog.Debugf("PeripheralManagerIsReadyToUpdateSubscribers: pmgr=%v") + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.IsReadyToUpdateSubscribers(PeripheralManager{pmgr}) + } +} + +//export BTPeripheralManagerDidReceiveReadRequest +func BTPeripheralManagerDidReceiveReadRequest(pmgr unsafe.Pointer, req unsafe.Pointer) { + btlog.Debugf("PeripheralManagerDidReceiveReadRequest: pmgr=%v req=%+v", pmgr, req) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.DidReceiveReadRequest(PeripheralManager{pmgr}, ATTRequest{req}) + } +} + +//export BTPeripheralManagerDidReceiveWriteRequests +func BTPeripheralManagerDidReceiveWriteRequests(pmgr unsafe.Pointer, oa *C.struct_obj_arr) { + reqs := make([]ATTRequest, oa.count) + for i, _ := range reqs { + ptr := getObjArrElem(oa, i) + reqs[i] = ATTRequest{ptr} + } + + btlog.Debugf("PeripheralManagerDidReceiveWriteRequests: pmgr=%v reqs=%v", pmgr, reqs) + + dlg := findPeripheralManagerDlg(pmgr) + if dlg != nil { + dlg.DidReceiveWriteRequests(PeripheralManager{pmgr}, reqs) + } +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/central.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/central.go new file mode 100644 index 0000000..9ea553e --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/central.go @@ -0,0 +1,25 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +import "unsafe" + +// Central: https://developer.apple.com/documentation/corebluetooth/cbcentral +type Central struct { + ptr unsafe.Pointer +} + +// Identifier: https://developer.apple.com/documentation/corebluetooth/cbpeer/1620687-identifier +func (c Central) Identifier() UUID { + cstr := C.cb_peer_identifier(c.ptr) + return MustParseUUID(C.GoString(cstr)) +} + +// MaximumUpdateValueLength: https://developer.apple.com/documentation/corebluetooth/cbcentral/1408800-maximumupdatevaluelength +func (c Central) MaximumUpdateValueLength() int { + return int(C.cb_cent_maximum_update_len(c.ptr)) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanager.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanager.go new file mode 100644 index 0000000..800f0b5 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanager.go @@ -0,0 +1,185 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +import ( + "unsafe" +) + +// CentralManagerRestoreOpts: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/central_manager_state_restoration_options +type CentralManagerRestoreOpts struct { + Peripherals []Peripheral + ScanServices []UUID + CentralManagerScanOpts *CentralManagerScanOpts // nil if none +} + +// CentralManagerScanOpts: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/peripheral_scanning_options +type CentralManagerScanOpts struct { + AllowDuplicates bool + SolicitedServiceUUIDs []UUID +} + +// DfltCentralManagerConnectOpts is the set of options that gets used when nil +// is passed to `Connect()`. +var DfltCentralManagerConnectOpts = CentralManagerConnectOpts{ + NotifyOnConnection: true, + NotifyOnDisconnection: true, + NotifyOnNotification: true, +} + +// DfltCentralManagerScanOpts is the set of options that gets used when nil is +// passed to `Scan()`. +var DfltCentralManagerScanOpts = CentralManagerScanOpts{} + +// CentralManagerConnectOpts: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/peripheral_connection_options +type CentralManagerConnectOpts struct { + NotifyOnConnection bool + NotifyOnDisconnection bool + NotifyOnNotification bool + EnableTransportBridging bool + RequiresANCS bool + StartDelay int +} + +// CentralManager: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager?language=objc +type CentralManager struct { + ptr unsafe.Pointer +} + +var cmgrPtrMap = newPtrMap() + +func findCentralManagerDlg(ptr unsafe.Pointer) CentralManagerDelegate { + itf := cmgrPtrMap.find(ptr) + if itf == nil { + return nil + } + + return itf.(CentralManagerDelegate) +} + +// NewCentralManager creates a central manager. Specify a nil `opts` value for +// defaults. Don't forget to call `SetDelegate()` afterwards! +func NewCentralManager(opts *ManagerOpts) CentralManager { + if opts == nil { + opts = &DfltManagerOpts + } + + pwrAlert := C.bool(opts.ShowPowerAlert) + + restoreID := (*C.char)(nil) + if opts.RestoreIdentifier != "" { + restoreID = C.CString(opts.RestoreIdentifier) + defer C.free(unsafe.Pointer(restoreID)) + } + + return CentralManager{ + ptr: unsafe.Pointer(C.cb_alloc_cmgr(pwrAlert, restoreID)), + } +} + +// SetDelegate configures a receiver for a central manager's asynchronous +// callbacks. +func (cm CentralManager) SetDelegate(d CentralManagerDelegate) { + if d != nil { + cmgrPtrMap.add(cm.ptr, d) + } + C.cb_cmgr_set_delegate(cm.ptr, C.bool(d != nil)) +} + +// State: https://developer.apple.com/documentation/corebluetooth/cbmanager/1648600-state +func (cm CentralManager) State() ManagerState { + return ManagerState(C.cb_cmgr_state(cm.ptr)) +} + +// Connect: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518766-connectperipheral +func (cm CentralManager) Connect(prph Peripheral, opts *CentralManagerConnectOpts) { + if opts == nil { + opts = &DfltCentralManagerConnectOpts + } + + copts := C.struct_connect_opts{ + notify_on_connection: C.bool(opts.NotifyOnConnection), + notify_on_disconnection: C.bool(opts.NotifyOnDisconnection), + notify_on_notification: C.bool(opts.NotifyOnNotification), + enable_transport_bridging: C.bool(opts.EnableTransportBridging), + requires_ancs: C.bool(opts.RequiresANCS), + start_delay: C.int(opts.StartDelay), + } + + C.cb_cmgr_connect(cm.ptr, prph.ptr, &copts) +} + +// CancelConnect: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518952-cancelperipheralconnection +func (cm CentralManager) CancelConnect(prph Peripheral) { + C.cb_cmgr_cancel_connect(cm.ptr, prph.ptr) +} + +// RetrieveConnectedPeripheralsWithServices: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518924-retrieveconnectedperipheralswith +func (cm CentralManager) RetrieveConnectedPeripheralsWithServices(uuids []UUID) []Peripheral { + strs := uuidsToStrArr(uuids) + defer freeStrArr(&strs) + + var prphs []Peripheral + + prphPtrs := C.cb_cmgr_retrieve_prphs_with_svcs(cm.ptr, &strs) + defer C.free(unsafe.Pointer(prphPtrs.objs)) + + for i := 0; i < int(prphPtrs.count); i++ { + ptr := getObjArrElem(&prphPtrs, i) + prphs = append(prphs, Peripheral{ptr}) + } + + return prphs +} + +// RetrievePeripheralsWithIdentifiers: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1519127-retrieveperipheralswithidentifie +func (cm CentralManager) RetrievePeripheralsWithIdentifiers(uuids []UUID) []Peripheral { + strs := uuidsToStrArr(uuids) + defer freeStrArr(&strs) + + var prphs []Peripheral + + prphPtrs := C.cb_cmgr_retrieve_prphs(cm.ptr, &strs) + defer C.free(unsafe.Pointer(prphPtrs.objs)) + + for i := 0; i < int(prphPtrs.count); i++ { + ptr := getObjArrElem(&prphPtrs, i) + prphs = append(prphs, Peripheral{ptr}) + } + + return prphs +} + +// Scan: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518986-scanforperipheralswithservices +func (cm CentralManager) Scan(serviceUUIDs []UUID, opts *CentralManagerScanOpts) { + arrSvcUUIDs := uuidsToStrArr(serviceUUIDs) + defer freeStrArr(&arrSvcUUIDs) + + if opts == nil { + opts = &DfltCentralManagerScanOpts + } + + arrSolSvcUUIDs := uuidsToStrArr(opts.SolicitedServiceUUIDs) + defer freeStrArr(&arrSolSvcUUIDs) + + copts := C.struct_scan_opts{ + allow_dups: C.bool(opts.AllowDuplicates), + sol_svc_uuids: arrSolSvcUUIDs, + } + + C.cb_cmgr_scan(cm.ptr, &arrSvcUUIDs, &copts) +} + +// StopScan: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518984-stopscan +func (cm CentralManager) StopScan() { + C.cb_cmgr_stop_scan(cm.ptr) +} + +// IsScanning: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1620640-isscanning +func (cm CentralManager) IsScanning() bool { + return bool(C.cb_cmgr_is_scanning(cm.ptr)) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanagerdelegate.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanagerdelegate.go new file mode 100644 index 0000000..46dd7d3 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/centralmanagerdelegate.go @@ -0,0 +1,41 @@ +package cbgo + +// CentralManagerDelegate: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate +type CentralManagerDelegate interface { + // DidConnectPeripheral: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518969-centralmanager + DidConnectPeripheral(cmgr CentralManager, prph Peripheral) + + // DidDisconnectPeripheral: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518791-centralmanager + DidDisconnectPeripheral(cmgr CentralManager, prph Peripheral, err error) + + // DidFailToConnectPeripheral: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518988-centralmanager + DidFailToConnectPeripheral(cmgr CentralManager, prph Peripheral, err error) + + // DidDiscoverPeripheral: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518937-centralmanager + DidDiscoverPeripheral(cmgr CentralManager, prph Peripheral, advFields AdvFields, rssi int) + + // CentralManagerDidUpdateState: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate + CentralManagerDidUpdateState(cmgr CentralManager) + + // CentralManagerWillRestoreState: https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518819-centralmanager + CentralManagerWillRestoreState(cmgr CentralManager, opts CentralManagerRestoreOpts) +} + +// CentralManagerDelegateBase implements the CentralManagerDelegate interface +// with stub functions. Embed this in your delegate type if you only want to +// define a subset of the CentralManagerDelegate interface. +type CentralManagerDelegateBase struct { +} + +func (b *CentralManagerDelegateBase) DidConnectPeripheral(cmgr CentralManager, prph Peripheral) { +} +func (b *CentralManagerDelegateBase) DidFailToConnectPeripheral(cmgr CentralManager, prph Peripheral, err error) { +} +func (b *CentralManagerDelegateBase) DidDisconnectPeripheral(cmgr CentralManager, prph Peripheral, err error) { +} +func (b *CentralManagerDelegateBase) CentralManagerDidUpdateState(cmgr CentralManager) { +} +func (b *CentralManagerDelegateBase) CentralManagerWillRestoreState(cmgr CentralManager, opts CentralManagerRestoreOpts) { +} +func (b *CentralManagerDelegateBase) DidDiscoverPeripheral(cmgr CentralManager, prph Peripheral, advFields AdvFields, rssi int) { +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/characteristic.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/characteristic.go new file mode 100644 index 0000000..f42d442 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/characteristic.go @@ -0,0 +1,80 @@ +package cbgo + +import "unsafe" + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// CharacteristicProperties: https://developer.apple.com/documentation/corebluetooth/cbcharacteristicproperties +type CharacteristicProperties int + +const ( + CharacteristicPropertyBroadcast = CharacteristicProperties(C.CBCharacteristicPropertyBroadcast) + CharacteristicPropertyRead = CharacteristicProperties(C.CBCharacteristicPropertyRead) + CharacteristicPropertyWriteWithoutResponse = CharacteristicProperties(C.CBCharacteristicPropertyWriteWithoutResponse) + CharacteristicPropertyWrite = CharacteristicProperties(C.CBCharacteristicPropertyWrite) + CharacteristicPropertyNotify = CharacteristicProperties(C.CBCharacteristicPropertyNotify) + CharacteristicPropertyIndicate = CharacteristicProperties(C.CBCharacteristicPropertyIndicate) + CharacteristicPropertyAuthenticatedSignedWrites = CharacteristicProperties(C.CBCharacteristicPropertyAuthenticatedSignedWrites) + CharacteristicPropertyExtendedProperties = CharacteristicProperties(C.CBCharacteristicPropertyExtendedProperties) + CharacteristicPropertyNotifyEncryptionRequired = CharacteristicProperties(C.CBCharacteristicPropertyNotifyEncryptionRequired) + CharacteristicPropertyIndicateEncryptionRequired = CharacteristicProperties(C.CBCharacteristicPropertyIndicateEncryptionRequired) +) + +func chrWriteType(withRsp bool) C.int { + if withRsp { + return C.CBCharacteristicWriteWithResponse + } else { + return C.CBCharacteristicWriteWithoutResponse + } +} + +// Characteristic: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic +type Characteristic struct { + ptr unsafe.Pointer +} + +// UUID: https://developer.apple.com/documentation/corebluetooth/cbattribute/1620638-uuid +func (c Characteristic) UUID() UUID { + cstr := C.cb_chr_uuid(c.ptr) + return MustParseUUID(C.GoString(cstr)) +} + +// Service: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic/1518728-service +func (c Characteristic) Service() Service { + ptr := C.cb_chr_service(c.ptr) + return Service{ptr} +} + +// Value: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic/1518878-value +func (c Characteristic) Value() []byte { + ba := C.cb_chr_value(c.ptr) + return byteArrToByteSlice(&ba) +} + +// Descriptors: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic/1518957-descriptors +func (c Characteristic) Descriptors() []Descriptor { + oa := C.cb_chr_descriptors(c.ptr) + defer C.free(unsafe.Pointer(oa.objs)) + + dscs := make([]Descriptor, oa.count) + for i, _ := range dscs { + obj := getObjArrElem(&oa, i) + dscs[i] = Descriptor{ptr: obj} + } + + return dscs +} + +// Properties: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic/1519010-properties +func (c Characteristic) Properties() CharacteristicProperties { + return CharacteristicProperties(C.cb_chr_properties(c.ptr)) +} + +// IsNotifying: https://developer.apple.com/documentation/corebluetooth/cbcharacteristic/1519057-isnotifying +func (c Characteristic) IsNotifying() bool { + return bool(C.cb_chr_is_notifying(c.ptr)) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/cmgr.m b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cmgr.m new file mode 100644 index 0000000..40e0c76 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cmgr.m @@ -0,0 +1,430 @@ +#import +#import +#import +#import "bt.h" + +// cmgr.m: C functions for interfacing with CoreBluetooth central +// functionality. This is necessary because Go code cannot execute some +// objective C constructs directly. + +CBCentralManager * +cb_alloc_cmgr(bool pwr_alert, const char *restore_id) +{ + // Ensure queue is initialized. + bt_init(); + + NSMutableDictionary *opts = [[NSMutableDictionary alloc] init]; + [opts autorelease]; + + if (pwr_alert) { + [opts setObject:[NSNumber numberWithBool:pwr_alert] + forKey:CBCentralManagerOptionShowPowerAlertKey]; + } + if (restore_id != NULL) { + [opts setObject:str_to_nsstring(restore_id) + forKey:CBCentralManagerOptionRestoreIdentifierKey]; + } + + CBCentralManager *cm = [[CBCentralManager alloc] initWithDelegate:bt_dlg + queue:bt_queue + options:opts]; + [cm retain]; + return cm; +} + +void +cb_cmgr_set_delegate(void *cmgr, bool set) +{ + BTDlg *del; + if (set) { + del = bt_dlg; + } else { + del = nil; + } + + ((CBCentralManager *)cmgr).delegate = del; +} + +int +cb_cmgr_state(void *cmgr) +{ + CBCentralManager *cm = cmgr; + return [cm state]; +} + +void +cb_cmgr_scan(void *cmgr, const struct string_arr *svc_uuids, + const struct scan_opts *opts) +{ + NSArray *arr_svc_uuids = strs_to_cbuuids(svc_uuids); + [arr_svc_uuids autorelease]; + + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + [dict autorelease]; + + if (opts->allow_dups) { + [dict setObject:[NSNumber numberWithBool:YES] + forKey:CBCentralManagerScanOptionAllowDuplicatesKey]; + } + + if (opts->sol_svc_uuids.count > 0) { + NSArray *arr_sol_svc_uuids = strs_to_cbuuids(&opts->sol_svc_uuids); + [arr_sol_svc_uuids autorelease]; + + [dict setObject:arr_sol_svc_uuids + forKey:CBCentralManagerScanOptionSolicitedServiceUUIDsKey]; + } + + CBCentralManager *cm = cmgr; + [cm scanForPeripheralsWithServices:arr_svc_uuids options:dict]; +} + +void +cb_cmgr_stop_scan(void *cmgr) +{ + [(CBCentralManager *)cmgr stopScan]; +} + +bool +cb_cmgr_is_scanning(void *cmgr) +{ + return ((CBCentralManager *)cmgr).isScanning; +} + +void +cb_cmgr_connect(void *cmgr, void *prph, const struct connect_opts *opts) +{ + CBCentralManager *cm = cmgr; + CBPeripheral *pr = prph; + + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + [dict autorelease]; + + if (opts->notify_on_connection) { + dict_set_bool(dict, CBConnectPeripheralOptionNotifyOnConnectionKey, true); + } + if (opts->notify_on_disconnection) { + dict_set_bool(dict, CBConnectPeripheralOptionNotifyOnDisconnectionKey, true); + } + if (opts->notify_on_notification) { + dict_set_bool(dict, CBConnectPeripheralOptionNotifyOnNotificationKey, true); + } + + // XXX: These don't seem to be supported in all versions of macOS. +#if 0 + if (opts->enable_transport_bridging) { + dict_set_bool(dict, CBConnectPeripheralOptionEnableTransportBridgingKey, true); + } + if (opts->requires_ancs) { + dict_set_bool(dict, CBConnectPeripheralOptionRequiresANCS, true); + } + if (opts->start_delay > 0) { + dict_set_int(dict, CBConnectPeripheralOptionStartDelayKey, opts->start_delay); + } +#endif + + [cm connectPeripheral:pr options:dict]; +} + +void +cb_cmgr_cancel_connect(void *cmgr, void *prph) +{ + CBCentralManager *cm = cmgr; + CBPeripheral *pr = prph; + + [cm cancelPeripheralConnection:pr]; +} + +struct obj_arr +cb_cmgr_retrieve_prphs_with_svcs(void *cmgr, const struct string_arr *uuids) +{ + CBCentralManager *cm = cmgr; + + NSArray *cbuuids = strs_to_cbuuids(uuids); + NSArray *prphs = [cm retrieveConnectedPeripheralsWithServices:cbuuids]; + + if ([prphs count] <= 0) { + return (struct obj_arr) {0}; + } + + void **objs = malloc([prphs count] * sizeof *objs); + assert(objs != NULL); + + for (int i = 0; i < [prphs count]; i++) { + objs[i] = [prphs objectAtIndex:i]; + } + + return (struct obj_arr) { + .objs = objs, + .count = [prphs count], + }; +} + +struct obj_arr +cb_cmgr_retrieve_prphs(void *cmgr, const struct string_arr *uuids) +{ + CBCentralManager *cm = cmgr; + + NSArray *nsuuids = strs_to_nsuuids(uuids); + NSArray *prphs = [cm retrievePeripheralsWithIdentifiers:nsuuids]; + + if ([prphs count] <= 0) { + return (struct obj_arr) {0}; + } + + void **objs = malloc([prphs count] * sizeof *objs); + assert(objs != NULL); + + for (int i = 0; i < [prphs count]; i++) { + objs[i] = [prphs objectAtIndex:i]; + } + + return (struct obj_arr) { + .objs = objs, + .count = [prphs count], + }; +} + +const char * +cb_peer_identifier(void *peer) +{ + NSUUID *uuid = [(CBPeer *)peer identifier]; + return [[uuid UUIDString] UTF8String]; +} + +void +cb_prph_set_delegate(void *prph, bool set) +{ + BTDlg *del; + if (set) { + del = bt_dlg; + } else { + del = nil; + } + + ((CBPeripheral *)prph).delegate = del; +} + +const char * +cb_prph_name(void *prph) +{ + NSString *nss = [(CBPeripheral *)prph name]; + return [nss UTF8String]; +} + +struct obj_arr +cb_prph_services(void *prph) +{ + NSArray *svcs = ((CBPeripheral *)prph).services; + return nsarray_to_obj_arr(svcs); +} + +void +cb_prph_discover_svcs(void *prph, const struct string_arr *svc_uuid_strs) +{ + NSArray *svc_uuids = strs_to_cbuuids(svc_uuid_strs); + [(CBPeripheral *)prph discoverServices:svc_uuids]; +} + +void +cb_prph_discover_included_svcs(void *prph, const struct string_arr *svc_uuid_strs, void *svc) +{ + NSArray *svc_uuids = strs_to_cbuuids(svc_uuid_strs); + [(CBPeripheral *)prph discoverIncludedServices:svc_uuids forService:svc]; +} + +void +cb_prph_discover_chrs(void *prph, void *svc, const struct string_arr *chr_uuid_strs) +{ + NSArray *chr_uuids = strs_to_cbuuids(chr_uuid_strs); + [(CBPeripheral *)prph discoverCharacteristics:chr_uuids forService:svc]; +} + +void +cb_prph_discover_dscs(void *prph, void *chr) +{ + [(CBPeripheral *)prph discoverDescriptorsForCharacteristic:chr]; +} + +void +cb_prph_read_chr(void *prph, void *chr) +{ + [(CBPeripheral *)prph readValueForCharacteristic:chr]; +} + +void +cb_prph_read_dsc(void *prph, void *dsc) +{ + [(CBPeripheral *)prph readValueForDescriptor:dsc]; +} + +void +cb_prph_write_chr(void *prph, void *chr, struct byte_arr *value, int type) +{ + NSData *nsd = byte_arr_to_nsdata(value); + [(CBPeripheral *)prph writeValue:nsd + forCharacteristic:chr + type:type]; +} + +void +cb_prph_write_dsc(void *prph, void *dsc, struct byte_arr *value) +{ + NSData *nsd = byte_arr_to_nsdata(value); + [(CBPeripheral *)prph writeValue:nsd + forDescriptor:dsc]; +} + +int +cb_prph_max_write_len(void *prph, int type) +{ + return [(CBPeripheral *)prph maximumWriteValueLengthForType:type]; +} + +void +cb_prph_set_notify(void *prph, bool enabled, void *chr) +{ + [(CBPeripheral *)prph setNotifyValue:enabled forCharacteristic:chr]; +} + +int +cb_prph_state(void *prph) +{ + return ((CBPeripheral *) prph).state; +} + +bool +cb_prph_can_send_write_without_rsp(void *prph) +{ + return ((CBPeripheral *)prph).canSendWriteWithoutResponse; +} + +void +cb_prph_read_rssi(void *prph) +{ + [(CBPeripheral *)prph readRSSI]; +} + +// error: 'openL2CAPChannel:' is unavailable: not available on macOS +#if 0 +void +cb_prph_open_l2cap_channel(void *prph, uint16_t cid) +{ + [(CBPeripheral *)prph openL2CAPChannel:cid]; +} +#endif + +// error: property 'ancsAuthorized' not found on object of type 'CBPeripheral *' +#if 0 +bool +cb_prph_ancs_authorized(void *prph) +{ + return ((CBPeripheral *)prph).ancsAuthorized; +} +#endif + +const char * +cb_svc_uuid(void *svc) +{ + CBUUID *cbuuid = ((CBService *)svc).UUID; + return [[cbuuid UUIDString] UTF8String]; +} + +void * +cb_svc_peripheral(void *svc) +{ + return ((CBService *)svc).peripheral; +} + +bool +cb_svc_is_primary(void *svc) +{ + return ((CBService *)svc).isPrimary; +} + +struct obj_arr +cb_svc_characteristics(void *svc) +{ + NSArray *chrs = ((CBService *)svc).characteristics; + return nsarray_to_obj_arr(chrs); +} + +struct obj_arr +cb_svc_included_svcs(void *svc) +{ + NSArray *svcs = ((CBService *)svc).includedServices; + return nsarray_to_obj_arr(svcs); +} + +const char * +cb_chr_uuid(void *chr) +{ + CBUUID *cbuuid = ((CBCharacteristic *)chr).UUID; + return [[cbuuid UUIDString] UTF8String]; +} + +void * +cb_chr_service(void *chr) +{ + return ((CBCharacteristic *)chr).service; +} + +struct obj_arr +cb_chr_descriptors(void *chr) +{ + NSArray *dscs = ((CBCharacteristic *)chr).descriptors; + return nsarray_to_obj_arr(dscs); +} + +struct byte_arr +cb_chr_value(void *chr) +{ + NSData *nsd = ((CBCharacteristic *)chr).value; + return nsdata_to_byte_arr(nsd); +} + +int +cb_chr_properties(void *chr) +{ + return ((CBCharacteristic *)chr).properties; +} + +bool +cb_chr_is_notifying(void *chr) +{ + return ((CBCharacteristic *)chr).isNotifying; +} + +const char * +cb_dsc_uuid(void *dsc) +{ + CBUUID *cbuuid = ((CBDescriptor *)dsc).UUID; + return [[cbuuid UUIDString] UTF8String]; +} + +void * +cb_dsc_characteristic(void *dsc) +{ + return ((CBDescriptor *)dsc).characteristic; +} + +struct byte_arr +cb_dsc_value(void *dsc) +{ + id val = ((CBDescriptor *)dsc).value; + + // `value` returns a different type depending on the descriptor being + // queried! Convert whatever got returned to a byte array. + + if ([val isKindOfClass:[NSData class]]) { + return nsdata_to_byte_arr(val); + } + + if ([val isKindOfClass:[NSString class]]) { + NSData *nsd = [val dataUsingEncoding:NSUTF8StringEncoding]; + return nsdata_to_byte_arr(nsd); + } + + // Unknown type. + return (struct byte_arr){0}; +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/core.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/core.go new file mode 100644 index 0000000..2c154db --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/core.go @@ -0,0 +1,143 @@ +package cbgo + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "strconv" +) + +const UUID16StringLength = 4 +const UUID128StringLength = 36 + +// UUID is a 16-bit or 128-bit UUID. +type UUID []byte + +func reverse(bs []byte) []byte { + c := make([]byte, len(bs)) + for i, b := range bs { + c[len(c)-1-i] = b + } + + return c +} + +// UUID16 constructs a 16-bit UUID from a uint16. +func UUID16(i uint16) UUID { + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, i) + return UUID(b) +} + +// UUID16 constructs a 128-bit UUID from a 16-element byte slice. +func UUID128(b []byte) (UUID, error) { + if len(b) != 16 { + return nil, fmt.Errorf("failed to construct UUID128: wrong length: have=%d want=16", len(b)) + } + + return UUID(b), nil +} + +// ParseUUID16 parses a UUID string with the form: +// 1234 +func ParseUUID16(s string) (UUID, error) { + if len(s) != UUID16StringLength { + return nil, fmt.Errorf("invalid UUID16: %s", s) + } + + b, err := hex.DecodeString(s) + if err != nil { + return nil, fmt.Errorf("invald UUID16: %v", err) + } + + return UUID(reverse(b)), nil +} + +// ParseUUID128 parses a UUID string with the form: +// 01234567-89ab-cdef-0123-456789abcdef +func ParseUUID128(s string) (UUID, error) { + if len(s) != UUID128StringLength { + return nil, fmt.Errorf("invalid UUID128: %s", s) + } + + b := make([]byte, 16) + + off := 0 + for i := 0; i < 36; { + switch i { + case 8, 13, 18, 23: + if s[i] != '-' { + return nil, fmt.Errorf("invalid UUID128: %s", s) + } + i++ + + default: + u64, err := strconv.ParseUint(s[i:i+2], 16, 8) + if err != nil { + return nil, fmt.Errorf("invalid UUID128: %s", s) + } + b[off] = byte(u64) + i += 2 + off++ + } + } + + return UUID(reverse(b)), nil +} + +// ParseUUID parses a string representing a 16-bit or 128-bit UUID. +func ParseUUID(s string) (UUID, error) { + switch len(s) { + case UUID16StringLength: + return ParseUUID16(s) + case UUID128StringLength: + return ParseUUID128(s) + default: + return nil, fmt.Errorf("invalid UUID string: %s", s) + } +} + +// MustParseUUID is like ParseUUID except it panics on failure. +func MustParseUUID(s string) UUID { + uuid, err := ParseUUID(s) + if err != nil { + panic(err) + } + + return uuid +} + +// String retruns a CoreBluetooth-friendly string representation of a UUID. +func (u UUID) String() string { + b := reverse(u) + + switch len(b) { + case 2: + return fmt.Sprintf("%x", []byte(b)) + + case 16: + s := fmt.Sprintf("%x", []byte(b)) + // 01234567-89ab-cdef-0123-456789abcdef + return s[:8] + "-" + s[8:12] + "-" + s[12:16] + "-" + s[16:20] + "-" + s[20:] + + default: + btlog.Errorf("invalid UUID: %s", hex.EncodeToString(u)) + return "" + } +} + +// NSError: https://developer.apple.com/documentation/foundation/nserror +type NSError struct { + msg string + code int +} + +func (e *NSError) Message() string { + return e.msg +} +func (e *NSError) Code() int { + return e.code +} +func (e *NSError) Error() string { + return e.msg +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/cutil.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cutil.go new file mode 100644 index 0000000..2cd6cb0 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/cutil.go @@ -0,0 +1,173 @@ +package cbgo + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework CoreBluetooth + +#import "bt.h" +*/ +import "C" + +import ( + "unsafe" +) + +func mallocArr(numElems int, elemSize uintptr) unsafe.Pointer { + return C.malloc(C.size_t(numElems) * C.size_t(elemSize)) +} + +// getArrElemAddr retrieves the address of an element from a C array. +func getArrElemAddr(arr unsafe.Pointer, elemSize uintptr, idx int) unsafe.Pointer { + base := uintptr(arr) + off := uintptr(idx) * elemSize + cur := base + off + return unsafe.Pointer(cur) +} + +// freeArrElems frees each element of a C-array of pointers. +func freeArrElems(arr unsafe.Pointer, elemSize uintptr, count int) { + for i := 0; i < count; i++ { + base := uintptr(arr) + off := uintptr(i) * elemSize + addr := base + off + pp := unsafe.Pointer(addr) + ptr := *(*unsafe.Pointer)(pp) + C.free(ptr) + } +} + +func byteArrToByteSlice(byteArr *C.struct_byte_arr) []byte { + return C.GoBytes(unsafe.Pointer(byteArr.data), byteArr.length) +} + +func byteSliceToByteArr(b []byte) C.struct_byte_arr { + if len(b) == 0 { + return C.struct_byte_arr{ + data: nil, + length: 0, + } + } else { + return C.struct_byte_arr{ + data: (*C.uint8_t)(C.CBytes(b)), + length: C.int(len(b)), + } + } +} + +func stringArrToStringSlice(sa *C.struct_string_arr) []string { + ss := make([]string, sa.count) + for i, _ := range ss { + ss[i] = getStrArrElem(sa, i) + } + + return ss +} + +func stringSliceToArr(ss []string) C.struct_string_arr { + if len(ss) == 0 { + return C.struct_string_arr{} + } + + ptr := mallocArr(len(ss), unsafe.Sizeof((*C.char)(nil))) + + carr := (*[1<<30 - 1]*C.char)(ptr) + for i, s := range ss { + carr[i] = C.CString(s) + } + + return C.struct_string_arr{ + strings: (**C.char)(ptr), + count: C.int(len(ss)), + } +} + +// getStrArrElem retrieves an element from a `struct string_arr` C object. +func getStrArrElem(sa *C.struct_string_arr, idx int) string { + elemSize := unsafe.Sizeof(*sa.strings) + + ptr := getArrElemAddr(unsafe.Pointer(sa.strings), elemSize, idx) + cstr := *(**C.char)(ptr) + return C.GoString(cstr) +} + +func uuidsToStrArr(uuids []UUID) C.struct_string_arr { + var ss []string + for _, u := range uuids { + ss = append(ss, u.String()) + } + + return stringSliceToArr(ss) +} + +func strArrToUUIDs(sa *C.struct_string_arr) ([]UUID, error) { + if sa == nil || sa.count == 0 { + return nil, nil + } + + uuids := make([]UUID, sa.count) + + ss := stringArrToStringSlice(sa) + for i, s := range ss { + uuid, err := ParseUUID(s) + if err != nil { + return nil, err + } + uuids[i] = uuid + } + + return uuids, nil +} + +func mustStrArrToUUIDs(sa *C.struct_string_arr) []UUID { + uuids, err := strArrToUUIDs(sa) + if err != nil { + panic(err) + } + + return uuids +} + +func freeStrArr(sa *C.struct_string_arr) { + freeArrElems(unsafe.Pointer(sa.strings), unsafe.Sizeof(*sa.strings), int(sa.count)) + C.free(unsafe.Pointer(sa.strings)) +} + +func btErrorToNSError(e *C.struct_bt_error) error { + if e == nil || e.msg == nil { + return nil + } else { + return &NSError{ + msg: C.GoString(e.msg), + code: int(e.code), + } + } +} + +func mallocObjArr(count int) C.struct_obj_arr { + if count <= 0 { + return C.struct_obj_arr{ + objs: nil, + count: 0, + } + } + + data := mallocArr(count, unsafe.Sizeof(unsafe.Pointer(nil))) + return C.struct_obj_arr{ + objs: (*unsafe.Pointer)(data), + count: C.int(count), + } +} + +// getStrArrElem retrieves an element from a `struct obj_arr` C object. +func getObjArrElem(oa *C.struct_obj_arr, idx int) unsafe.Pointer { + addr := getArrElemAddr(unsafe.Pointer(oa.objs), unsafe.Sizeof(*oa.objs), idx) + dptr := (*unsafe.Pointer)(addr) + return *dptr +} + +// setStrArrElem assigns an element in a `struct obj_arr` C object. +func setObjArrElem(oa *C.struct_obj_arr, idx int, val unsafe.Pointer) { + addr := getArrElemAddr(unsafe.Pointer(oa.objs), unsafe.Sizeof(*oa.objs), idx) + dptr := (*unsafe.Pointer)(addr) + *dptr = val +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/descriptor.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/descriptor.go new file mode 100644 index 0000000..740469e --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/descriptor.go @@ -0,0 +1,32 @@ +package cbgo + +import "unsafe" + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// Descriptor: https://developer.apple.com/documentation/corebluetooth/cbdescriptor +type Descriptor struct { + ptr unsafe.Pointer +} + +// UUID: https://developer.apple.com/documentation/corebluetooth/cbattribute/1620638-uuid +func (d Descriptor) UUID() UUID { + cstr := C.cb_dsc_uuid(d.ptr) + return MustParseUUID(C.GoString(cstr)) +} + +// Characteristic: https://developer.apple.com/documentation/corebluetooth/cbdescriptor/1519035-characteristic +func (d Descriptor) Characteristic() Characteristic { + ptr := C.cb_dsc_characteristic(d.ptr) + return Characteristic{ptr} +} + +// Value: https://developer.apple.com/documentation/corebluetooth/cbdescriptor/1518778-value +func (d Descriptor) Value() []byte { + ba := C.cb_dsc_value(d.ptr) + return byteArrToByteSlice(&ba) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/manager.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/manager.go new file mode 100644 index 0000000..b0f7b95 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/manager.go @@ -0,0 +1,32 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// ManagerState: https://developer.apple.com/documentation/corebluetooth/cbmanagerstate +type ManagerState int + +const ( + ManagerStatePoweredOff = ManagerState(C.CBManagerStatePoweredOff) + ManagerStatePoweredOn = ManagerState(C.CBManagerStatePoweredOn) + ManagerStateResetting = ManagerState(C.CBManagerStateResetting) + ManagerStateUnauthorized = ManagerState(C.CBManagerStateUnauthorized) + ManagerStateUnknown = ManagerState(C.CBManagerStateUnknown) + ManagerStateUnsupported = ManagerState(C.CBManagerStateUnsupported) +) + +// ManagerOpts: https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/central_manager_initialization_options +type ManagerOpts struct { + ShowPowerAlert bool + RestoreIdentifier string +} + +// DfltManagerOpts is the set of options that gets used when nil is +// passed to `NewCentralManager()`. +var DfltManagerOpts = ManagerOpts{ + ShowPowerAlert: false, + RestoreIdentifier: "", +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutablecharacteristic.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutablecharacteristic.go new file mode 100644 index 0000000..54841a0 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutablecharacteristic.go @@ -0,0 +1,54 @@ +package cbgo + +import "unsafe" + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// MutableCharacteristic: https://developer.apple.com/documentation/corebluetooth/cbmutablecharacteristic +type MutableCharacteristic struct { + ptr unsafe.Pointer +} + +func NewMutableCharacteristic(uuid UUID, properties CharacteristicProperties, + value []byte, permissions AttributePermissions) MutableCharacteristic { + + cuuid := C.CString(uuid.String()) + defer C.free(unsafe.Pointer(cuuid)) + + cvalue := byteSliceToByteArr(value) + defer C.free(unsafe.Pointer(cvalue.data)) + + return MutableCharacteristic{ + ptr: unsafe.Pointer(C.cb_mchr_alloc(cuuid, C.int(properties), &cvalue, C.int(permissions))), + } +} + +// Characteristic converts a MutableCharacteristic into its underlying +// Characteristic. +func (c MutableCharacteristic) Characteristic() Characteristic { + return Characteristic{c.ptr} +} + +// SetDescriptors: https://developer.apple.com/documentation/corebluetooth/cbmutablecharacteristic/1518827-descriptors +func (c MutableCharacteristic) SetDescriptors(mdscs []MutableDescriptor) { + dscs := mallocObjArr(len(mdscs)) + defer C.free(unsafe.Pointer(dscs.objs)) + + for i, mdsc := range mdscs { + setObjArrElem(&dscs, i, mdsc.ptr) + } + + C.cb_mchr_set_descriptors(c.ptr, &dscs) +} + +// SetValue: https://developer.apple.com/documentation/corebluetooth/cbmutablecharacteristic/1519121-value +func (c MutableCharacteristic) SetValue(val []byte) { + ba := byteSliceToByteArr(val) + defer C.free(unsafe.Pointer(ba.data)) + + C.cb_mchr_set_value(c.ptr, &ba) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutabledescriptor.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutabledescriptor.go new file mode 100644 index 0000000..542205b --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutabledescriptor.go @@ -0,0 +1,31 @@ +package cbgo + +import "unsafe" + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// MutableDescriptor: https://developer.apple.com/documentation/corebluetooth/cbmutabledescriptor +type MutableDescriptor struct { + ptr unsafe.Pointer +} + +func NewMutableDescriptor(uuid UUID, value []byte) MutableDescriptor { + cuuid := C.CString(uuid.String()) + defer C.free(unsafe.Pointer(cuuid)) + + cvalue := byteSliceToByteArr(value) + defer C.free(unsafe.Pointer(cvalue.data)) + + return MutableDescriptor{ + ptr: unsafe.Pointer(C.cb_mdsc_alloc(cuuid, &cvalue)), + } +} + +// Descriptor converts a MutableDescriptor into its underlying Descriptor. +func (d MutableDescriptor) Descriptor() Descriptor { + return Descriptor{d.ptr} +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutableservice.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutableservice.go new file mode 100644 index 0000000..9e3d98e --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/mutableservice.go @@ -0,0 +1,52 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +import "unsafe" + +// MutableService: https://developer.apple.com/documentation/corebluetooth/cbmutableservice +type MutableService struct { + ptr unsafe.Pointer +} + +func NewMutableService(uuid UUID, primary bool) MutableService { + cuuid := C.CString(uuid.String()) + defer C.free(unsafe.Pointer(cuuid)) + + return MutableService{ + ptr: unsafe.Pointer(C.cb_msvc_alloc(cuuid, C.bool(primary))), + } +} + +// Service converts a MutableService into its underlying Service. +func (s MutableService) Service() Service { + return Service{s.ptr} +} + +// SetCharacteristics: https://developer.apple.com/documentation/corebluetooth/cbmutableservice/1434317-characteristics +func (s MutableService) SetCharacteristics(mchrs []MutableCharacteristic) { + chrs := mallocObjArr(len(mchrs)) + defer C.free(unsafe.Pointer(chrs.objs)) + + for i, mchr := range mchrs { + setObjArrElem(&chrs, i, mchr.ptr) + } + + C.cb_msvc_set_characteristics(s.ptr, &chrs) +} + +// SetIncludedServices: https://developer.apple.com/documentation/corebluetooth/cbmutableservice/1434320-includedservices +func (s MutableService) SetIncludedServices(msvcs []MutableService) { + svcs := mallocObjArr(len(msvcs)) + defer C.free(unsafe.Pointer(svcs.objs)) + + for i, msvc := range msvcs { + setObjArrElem(&svcs, i, msvc.ptr) + } + + C.cb_msvc_set_included_services(s.ptr, &svcs) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheral.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheral.go new file mode 100644 index 0000000..8923362 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheral.go @@ -0,0 +1,154 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +import ( + "unsafe" +) + +// PeripheralState: https://developer.apple.com/documentation/corebluetooth/cbperipheralstate +type PeripheralState int + +const ( + PeripheralStateDisconnected = PeripheralState(C.CBPeripheralStateDisconnected) + PeripheralStateConnecting = PeripheralState(C.CBPeripheralStateConnecting) + PeripheralStateConnected = PeripheralState(C.CBPeripheralStateConnected) + PeripheralStateDisconnecting = PeripheralState(C.CBPeripheralStateDisconnecting) +) + +var prphPtrMap = newPtrMap() + +// Peripheral: https://developer.apple.com/documentation/corebluetooth/cbperipheral +type Peripheral struct { + ptr unsafe.Pointer +} + +func findPeripheralDlg(ptr unsafe.Pointer) PeripheralDelegate { + itf := prphPtrMap.find(ptr) + if itf == nil { + return nil + } + + return itf.(PeripheralDelegate) +} + +func addPeripheralDlg(ptr unsafe.Pointer, dlg PeripheralDelegate) { + prphPtrMap.add(ptr, dlg) +} + +// Identifier: https://developer.apple.com/documentation/corebluetooth/cbpeer/1620687-identifier +func (p Peripheral) Identifier() UUID { + cstr := C.cb_peer_identifier(p.ptr) + return MustParseUUID(C.GoString(cstr)) +} + +// Name: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name +func (p Peripheral) Name() string { + cstr := C.cb_prph_name(p.ptr) + return C.GoString(cstr) +} + +// SetDelegate: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518730-delegate +func (p Peripheral) SetDelegate(dlg PeripheralDelegate) { + addPeripheralDlg(p.ptr, dlg) + C.cb_prph_set_delegate(p.ptr, C.bool(dlg != nil)) +} + +// DiscoverServices: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518706-discoverservices +func (p Peripheral) DiscoverServices(svcUUIDs []UUID) { + arr := uuidsToStrArr(svcUUIDs) + defer freeStrArr(&arr) + + C.cb_prph_discover_svcs(p.ptr, &arr) +} + +// DiscoverIncludedServices: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519014-discoverincludedservices +func (p Peripheral) DiscoverIncludedServices(svcUUIDs []UUID, svc Service) { + arr := uuidsToStrArr(svcUUIDs) + defer freeStrArr(&arr) + + C.cb_prph_discover_included_svcs(p.ptr, &arr, svc.ptr) +} + +// Services: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518978-services +func (p Peripheral) Services() []Service { + oa := C.cb_prph_services(p.ptr) + defer C.free(unsafe.Pointer(oa.objs)) + + svcs := make([]Service, oa.count) + for i, _ := range svcs { + obj := getObjArrElem(&oa, i) + svcs[i] = Service{ptr: obj} + } + + return svcs +} + +// DiscoverCharacteristics: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518797-discovercharacteristics +func (p Peripheral) DiscoverCharacteristics(chrUUIDs []UUID, svc Service) { + arr := uuidsToStrArr(chrUUIDs) + defer freeStrArr(&arr) + + C.cb_prph_discover_chrs(p.ptr, svc.ptr, &arr) +} + +// DiscoverDescriptors: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519070-discoverdescriptorsforcharacteri +func (p Peripheral) DiscoverDescriptors(chr Characteristic) { + C.cb_prph_discover_dscs(p.ptr, chr.ptr) +} + +// ReadCharacteristic: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518759-readvalueforcharacteristic +func (p Peripheral) ReadCharacteristic(chr Characteristic) { + C.cb_prph_read_chr(p.ptr, chr.ptr) +} + +// ReadDescriptor: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518789-readvaluefordescriptor +func (p Peripheral) ReadDescriptor(dsc Descriptor) { + C.cb_prph_read_dsc(p.ptr, dsc.ptr) +} + +// WriteCharacteristic: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518747-writevalue +func (p Peripheral) WriteCharacteristic(data []byte, chr Characteristic, withRsp bool) { + ba := byteSliceToByteArr(data) + defer C.free(unsafe.Pointer(ba.data)) + + C.cb_prph_write_chr(p.ptr, chr.ptr, &ba, chrWriteType(withRsp)) +} + +// WriteDescriptor: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519107-writevalue +func (p Peripheral) WriteDescriptor(data []byte, dsc Descriptor) { + ba := byteSliceToByteArr(data) + defer C.free(unsafe.Pointer(ba.data)) + + C.cb_prph_write_dsc(p.ptr, dsc.ptr, &ba) +} + +// MaximumWriteValueLength: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1620312-maximumwritevaluelengthfortype +func (p Peripheral) MaximumWriteValueLength(withRsp bool) int { + val := C.cb_prph_max_write_len(p.ptr, chrWriteType(withRsp)) + return int(val) +} + +// SetNotify: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518949-setnotifyvalue +func (p Peripheral) SetNotify(enabled bool, chr Characteristic) { + C.cb_prph_set_notify(p.ptr, C.bool(enabled), chr.ptr) +} + +// PeripheralState: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519113-state +func (p Peripheral) State() PeripheralState { + return PeripheralState(C.cb_prph_state(p.ptr)) +} + +// CanSendWriteWithoutResponse: https://developer.apple.com/documentation/corebluetooth/cbperipheral/2891512-cansendwritewithoutresponse +func (p Peripheral) CanSendWriteWithoutResponse() bool { + return bool(C.cb_prph_can_send_write_without_rsp(p.ptr)) +} + +// ReadRSSI: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519111-readrssi +func (p Peripheral) ReadRSSI() { + C.cb_prph_read_rssi(p.ptr) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheraldelegate.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheraldelegate.go new file mode 100644 index 0000000..414c292 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheraldelegate.go @@ -0,0 +1,76 @@ +package cbgo + +// PeripheralDelegate: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate +type PeripheralDelegate interface { + // DidDiscoverServices: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518744-peripheral + DidDiscoverServices(prph Peripheral, err error) + + // DidDiscoverIncludedServices: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1519124-peripheral + DidDiscoverIncludedServices(prph Peripheral, svc Service, err error) + + // DidDiscoverCharacteristics: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518821-peripheral + DidDiscoverCharacteristics(prph Peripheral, svc Service, err error) + + // DidDiscoverDescriptors: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518785-peripheral + DidDiscoverDescriptors(prph Peripheral, chr Characteristic, err error) + + // DidUpdateValueForCharacteristic: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518708-peripheral + DidUpdateValueForCharacteristic(prph Peripheral, chr Characteristic, err error) + + // DidUpdateValueForDescriptor: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518929-peripheral + DidUpdateValueForDescriptor(prph Peripheral, dsc Descriptor, err error) + + // DidWriteValueForCharacteristic: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518823-peripheral + DidWriteValueForCharacteristic(prph Peripheral, chr Characteristic, err error) + + // DidWriteValueForDescriptor: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1519062-peripheral + DidWriteValueForDescriptor(prph Peripheral, dsc Descriptor, err error) + + // IsReadyToSendWriteWithoutResponse: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/2874034-peripheralisreadytosendwritewith + IsReadyToSendWriteWithoutResponse(prph Peripheral) + + // DidUpdateNotificationState: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518768-peripheral + DidUpdateNotificationState(prph Peripheral, chr Characteristic, err error) + + // DidReadRSSI: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1620304-peripheral + DidReadRSSI(prph Peripheral, rssi int, err error) + + // DidUpdateName: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518801-peripheraldidupdatename + DidUpdateName(prph Peripheral) + + // DidModifyServices: https://developer.apple.com/documentation/corebluetooth/cbperipheraldelegate/1518865-peripheral + DidModifyServices(prph Peripheral, invSvcs []Service) +} + +// PeripheralDelegateBase implements the PeripheralDelegate interface with stub +// functions. Embed this in your delegate type if you only want to define a +// subset of the PeripheralDelegate interface. +type PeripheralDelegateBase struct { +} + +func (b *PeripheralDelegateBase) DidDiscoverServices(prph Peripheral, err error) { +} +func (b *PeripheralDelegateBase) DidDiscoverIncludedServices(prph Peripheral, svc Service, err error) { +} +func (b *PeripheralDelegateBase) DidDiscoverCharacteristics(prph Peripheral, svc Service, err error) { +} +func (b *PeripheralDelegateBase) DidDiscoverDescriptors(prph Peripheral, chr Characteristic, err error) { +} +func (b *PeripheralDelegateBase) DidUpdateValueForCharacteristic(prph Peripheral, chr Characteristic, err error) { +} +func (b *PeripheralDelegateBase) DidUpdateValueForDescriptor(prph Peripheral, dsc Descriptor, err error) { +} +func (b *PeripheralDelegateBase) DidWriteValueForCharacteristic(prph Peripheral, chr Characteristic, err error) { +} +func (b *PeripheralDelegateBase) DidWriteValueForDescriptor(prph Peripheral, dsc Descriptor, err error) { +} +func (b *PeripheralDelegateBase) IsReadyToSendWriteWithoutResponse(prph Peripheral) { +} +func (b *PeripheralDelegateBase) DidUpdateNotificationState(prph Peripheral, chr Characteristic, err error) { +} +func (b *PeripheralDelegateBase) DidReadRSSI(prph Peripheral, rssi int, err error) { +} +func (b *PeripheralDelegateBase) DidUpdateName(prph Peripheral) { +} +func (b *PeripheralDelegateBase) DidModifyServices(prph Peripheral, invSvcs []Service) { +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanager.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanager.go new file mode 100644 index 0000000..02f5428 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanager.go @@ -0,0 +1,146 @@ +package cbgo + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +import ( + "unsafe" +) + +// PeripheralManagerConnectionLatency: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerconnectionlatency +type PeripheralManagerConnectionLatency int + +const ( + PeripheralManagerConnectionLatencyLow = PeripheralManagerConnectionLatency(C.CBPeripheralManagerConnectionLatencyLow) + PeripheralManagerConnectionLatencyMedium = PeripheralManagerConnectionLatency(C.CBPeripheralManagerConnectionLatencyMedium) + PeripheralManagerConnectionLatencyHigh = PeripheralManagerConnectionLatency(C.CBPeripheralManagerConnectionLatencyHigh) +) + +// PeripheralManagerRestoreOpts: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/peripheral_manager_state_restoration_options +type PeripheralManagerRestoreOpts struct { + Services []MutableService + AdvertisementData *AdvData +} + +// PeripheralManager: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager +type PeripheralManager struct { + ptr unsafe.Pointer +} + +var pmgrPtrMap = newPtrMap() + +func findPeripheralManagerDlg(ptr unsafe.Pointer) PeripheralManagerDelegate { + itf := pmgrPtrMap.find(ptr) + if itf == nil { + return nil + } + + return itf.(PeripheralManagerDelegate) +} + +// NewPeripheralManager creates a peripheral manager. Specify a nil `opts` +// value for defaults. Don't forget to call `SetDelegate()` afterwards! +func NewPeripheralManager(opts *ManagerOpts) PeripheralManager { + if opts == nil { + opts = &DfltManagerOpts + } + + pwrAlert := C.bool(opts.ShowPowerAlert) + + restoreID := (*C.char)(nil) + if opts.RestoreIdentifier != "" { + restoreID = C.CString(opts.RestoreIdentifier) + defer C.free(unsafe.Pointer(restoreID)) + } + + return PeripheralManager{ + ptr: unsafe.Pointer(C.cb_alloc_pmgr(pwrAlert, restoreID)), + } +} + +// SetDelegate configures a receiver for a central manager's asynchronous +// callbacks. +func (pm PeripheralManager) SetDelegate(d PeripheralManagerDelegate) { + if d != nil { + pmgrPtrMap.add(pm.ptr, d) + } + C.cb_pmgr_set_delegate(pm.ptr, C.bool(d != nil)) +} + +// State: https://developer.apple.com/documentation/corebluetooth/cbmanager/1648600-state +func (pm PeripheralManager) State() ManagerState { + return ManagerState(C.cb_pmgr_state(pm.ptr)) +} + +// AddService: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393255-addservice +func (pm PeripheralManager) AddService(svc MutableService) { + C.cb_pmgr_add_svc(pm.ptr, svc.ptr) +} + +// RemoveService: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393287-removeservice +func (pm PeripheralManager) RemoveService(svc MutableService) { + C.cb_pmgr_remove_svc(pm.ptr, svc.ptr) +} + +// RemoveAllServices: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393269-removeallservices +func (pm PeripheralManager) RemoveAllServices() { + C.cb_pmgr_remove_all_svcs(pm.ptr) +} + +// StartAdvertising: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising +func (pm PeripheralManager) StartAdvertising(ad AdvData) { + cad := C.struct_adv_data{} + + if len(ad.IBeaconData) > 0 { + cad.ibeacon_data = byteSliceToByteArr(ad.IBeaconData) + defer C.free(unsafe.Pointer(cad.ibeacon_data.data)) + } else { + if len(ad.LocalName) > 0 { + cad.name = C.CString(ad.LocalName) + defer C.free(unsafe.Pointer(cad.name)) + } + + cad.svc_uuids = uuidsToStrArr(ad.ServiceUUIDs) + defer freeStrArr(&cad.svc_uuids) + } + + C.cb_pmgr_start_adv(pm.ptr, &cad) +} + +// StopAdvertising: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393275-stopadvertising +func (pm PeripheralManager) StopAdvertising() { + C.cb_pmgr_stop_adv(pm.ptr) +} + +// IsAdvertising: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393291-isadvertising +func (pm PeripheralManager) IsAdvertising() bool { + return bool(C.cb_pmgr_is_adv(pm.ptr)) +} + +// UpdateValue: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393281-updatevalue +func (pm PeripheralManager) UpdateValue(value []byte, chr Characteristic, centrals []Central) bool { + ba := byteSliceToByteArr(value) + defer C.free(unsafe.Pointer(ba.data)) + + oa := mallocObjArr(len(centrals)) + defer C.free(unsafe.Pointer(oa.objs)) + + for i, c := range centrals { + setObjArrElem(&oa, i, c.ptr) + } + + return bool(C.cb_pmgr_update_val(pm.ptr, &ba, chr.ptr, &oa)) +} + +// RespondToRequest: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393293-respondtorequest +func (pm PeripheralManager) RespondToRequest(req ATTRequest, result ATTError) { + C.cb_pmgr_respond_to_req(pm.ptr, req.ptr, C.int(result)) +} + +// SetDesiredConnectionLatency: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393277-setdesiredconnectionlatency +func (pm PeripheralManager) SetDesiredConnectionLatency(latency PeripheralManagerConnectionLatency, central Central) { + C.cb_pmgr_set_conn_latency(pm.ptr, C.int(latency), central.ptr) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanagerdelegate.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanagerdelegate.go new file mode 100644 index 0000000..6491ee8 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/peripheralmanagerdelegate.go @@ -0,0 +1,56 @@ +package cbgo + +// https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate +type PeripheralManagerDelegate interface { + // PeripheralManagerDidUpdateState: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393271-peripheralmanagerdidupdatestate + PeripheralManagerDidUpdateState(pmgr PeripheralManager) + + // PeripheralManagerWillRestoreState: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393317-peripheralmanager + PeripheralManagerWillRestoreState(pmgr PeripheralManager, opts PeripheralManagerRestoreOpts) + + // DidAddService: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393279-peripheralmanager + DidAddService(pmgr PeripheralManager, svc Service, err error) + + // DidStartAdvertising: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393321-peripheralmanagerdidstartadverti + DidStartAdvertising(pmgr PeripheralManager, err error) + + // CentralDidSubscribe: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393261-peripheralmanager + CentralDidSubscribe(pmgr PeripheralManager, cent Central, chr Characteristic) + + // CentralDidUnsubscribe: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393289-peripheralmanager + CentralDidUnsubscribe(pmgr PeripheralManager, cent Central, chr Characteristic) + + // IsReadyToUpdateSubscribers: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393248-peripheralmanagerisreadytoupdate + IsReadyToUpdateSubscribers(pmgr PeripheralManager) + + // DidReceiveReadRequest: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393257-peripheralmanager + DidReceiveReadRequest(pmgr PeripheralManager, req ATTRequest) + + // DidReceiveWriteRequests: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393315-peripheralmanager + DidReceiveWriteRequests(pmgr PeripheralManager, reqs []ATTRequest) +} + +// PeripheralManagerDelegateBase implements the PeripheralManagerDelegate +// interface with stub functions. Embed this in your delegate type if you only +// want to define a subset of the PeripheralManagerDelegate interface. +type PeripheralManagerDelegateBase struct { +} + +func (b *PeripheralManagerDelegateBase) PeripheralManagerDidUpdateState(pmgr PeripheralManager) { +} +func (b *PeripheralManagerDelegateBase) PeripheralManagerWillRestoreState(pmgr PeripheralManager, opts PeripheralManagerRestoreOpts) { +} +func (b *PeripheralManagerDelegateBase) DidAddService(pmgr PeripheralManager, svc Service, err error) { +} +func (b *PeripheralManagerDelegateBase) DidStartAdvertising(pmgr PeripheralManager, err error) { +} +func (b *PeripheralManagerDelegateBase) CentralDidSubscribe(pmgr PeripheralManager, cent Central, chr Characteristic) { +} +func (b *PeripheralManagerDelegateBase) CentralDidUnsubscribe(pmgr PeripheralManager, cent Central, chr Characteristic) { +} +func (b *PeripheralManagerDelegateBase) IsReadyToUpdateSubscribers(pmgr PeripheralManager) { +} +func (b *PeripheralManagerDelegateBase) DidReceiveReadRequest(pmgr PeripheralManager, req ATTRequest) { +} +func (b *PeripheralManagerDelegateBase) DidReceiveWriteRequests(pmgr PeripheralManager, reqs []ATTRequest) { +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/pmgr.m b/cli/vendor/github.com/JuulLabs-OSS/cbgo/pmgr.m new file mode 100644 index 0000000..2653b5c --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/pmgr.m @@ -0,0 +1,252 @@ +#import +#import +#import +#import "bt.h" + +// pmgr.m: C functions for interfacing with CoreBluetooth peripheral +// functionality. This is necessary because Go code cannot execute some +// objective C constructs directly. + +CBPeripheralManager * +cb_alloc_pmgr(bool pwr_alert, const char *restore_id) +{ + // Ensure queue is initialized. + bt_init(); + + NSMutableDictionary *opts = [[NSMutableDictionary alloc] init]; + + if (pwr_alert) { + [opts setObject:[NSNumber numberWithBool:pwr_alert] + forKey:CBPeripheralManagerOptionShowPowerAlertKey]; + } + if (restore_id != NULL) { + [opts setObject:str_to_nsstring(restore_id) + forKey:CBPeripheralManagerOptionRestoreIdentifierKey]; + } + + CBPeripheralManager *pm = [[CBPeripheralManager alloc] initWithDelegate:bt_dlg + queue:bt_queue + options:opts]; + [pm retain]; + return pm; +} + +void +cb_pmgr_set_delegate(void *pmgr, bool set) +{ + BTDlg *del; + if (set) { + del = bt_dlg; + } else { + del = nil; + } + + ((CBPeripheralManager *)pmgr).delegate = del; +} + +int +cb_pmgr_state(void *pmgr) +{ + CBPeripheralManager *pm = pmgr; + return [pm state]; +} + +void +cb_pmgr_add_svc(void *pmgr, void *svc) +{ + [(CBPeripheralManager *)pmgr addService:svc]; +} + +void +cb_pmgr_remove_svc(void *pmgr, void *svc) +{ + [(CBPeripheralManager *)pmgr removeService:svc]; +} + +void +cb_pmgr_remove_all_svcs(void *pmgr) +{ + [(CBPeripheralManager *)pmgr removeAllServices]; +} + +void +cb_pmgr_start_adv(void *pmgr, const struct adv_data *ad) +{ + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + [dict autorelease]; + + // iBeacon data is mutually exclusing with the rest of the fields. + if (ad->ibeacon_data.length > 0) { + NSData *nsd = byte_arr_to_nsdata(&ad->ibeacon_data); + [nsd autorelease]; + + [dict setObject:nsd forKey:@"kCBAdvDataAppleBeaconKey"]; + } else { + if (ad->name != NULL) { + NSString *nss = str_to_nsstring(ad->name); + [nss autorelease]; + + [dict setObject:nss + forKey:CBAdvertisementDataLocalNameKey]; + } + + if (ad->svc_uuids.count > 0) { + NSArray *arr_svc_uuids = strs_to_cbuuids(&ad->svc_uuids); + [arr_svc_uuids autorelease]; + + [dict setObject:arr_svc_uuids + forKey:CBAdvertisementDataServiceUUIDsKey]; + } + } + + [(CBPeripheralManager *)pmgr startAdvertising:dict]; +} + +void +cb_pmgr_stop_adv(void *pmgr) +{ + [(CBPeripheralManager *)pmgr stopAdvertising]; +} + +bool +cb_pmgr_is_adv(void *pmgr) +{ + return [(CBPeripheralManager *)pmgr isAdvertising]; +} + +bool +cb_pmgr_update_val(void *pmgr, const struct byte_arr *value, void *chr, const struct obj_arr *centrals) +{ + NSData *nsd = byte_arr_to_nsdata(value); + [nsd autorelease]; + + NSArray *nsa = obj_arr_to_nsarray(centrals); + [nsa autorelease]; + + return [(CBPeripheralManager *)pmgr updateValue:nsd + forCharacteristic:chr + onSubscribedCentrals:nsa]; +} + +void +cb_pmgr_respond_to_req(void *pmgr, void *req, int result) +{ + [(CBPeripheralManager *)pmgr respondToRequest:req withResult:result]; +} + +void +cb_pmgr_set_conn_latency(void *pmgr, int latency, void *central) +{ + [(CBPeripheralManager *)pmgr setDesiredConnectionLatency:latency forCentral:central]; +} + +int +cb_cent_maximum_update_len(void *cent) +{ + return ((CBCentral *)cent).maximumUpdateValueLength; +} + +CBMutableService * +cb_msvc_alloc(const char *uuid, bool primary) +{ + CBUUID *cbuuid = str_to_cbuuid(uuid); + + CBMutableService *svc = [[CBMutableService alloc] initWithType:cbuuid primary:primary]; + + [svc retain]; + return svc; +} + +void +cb_msvc_set_characteristics(void *msvc, const struct obj_arr *mchrs) +{ + NSArray *nsa = obj_arr_to_nsarray(mchrs); + [nsa autorelease]; + + ((CBMutableService *)msvc).characteristics = nsa; +} + +void +cb_msvc_set_included_services(void *msvc, const struct obj_arr *msvcs) +{ + NSArray *nsa = obj_arr_to_nsarray(msvcs); + [nsa autorelease]; + + ((CBMutableService *)msvc).includedServices = nsa; +} + +CBMutableCharacteristic * +cb_mchr_alloc(const char *uuid, int properties, const struct byte_arr *value, int permissions) +{ + CBUUID *cbuuid = str_to_cbuuid(uuid); + NSData *nsd = byte_arr_to_nsdata(value); + + CBMutableCharacteristic *chr = [[CBMutableCharacteristic alloc] initWithType:cbuuid + properties:properties + value:nsd + permissions:permissions]; + + [chr retain]; + return chr; +} + +void +cb_mchr_set_descriptors(void *mchr, const struct obj_arr *mdscs) +{ + NSArray *nsa = obj_arr_to_nsarray(mdscs); + [nsa autorelease]; + + ((CBMutableCharacteristic *)mchr).descriptors = nsa; +} + +void +cb_mchr_set_value(void *mchr, const struct byte_arr *val) +{ + ((CBMutableCharacteristic *)mchr).value = byte_arr_to_nsdata(val); +} + + +CBMutableDescriptor * +cb_mdsc_alloc(const char *uuid, const struct byte_arr *value) +{ + CBUUID *cbuuid = str_to_cbuuid(uuid); + NSData *nsd = byte_arr_to_nsdata(value); + + CBMutableDescriptor *dsc = [[CBMutableDescriptor alloc] initWithType:cbuuid + value:nsd]; + + [dsc retain]; + return dsc; +} + +CBCentral * +cb_atr_central(void *atr) +{ + return ((CBATTRequest *)atr).central; +} + +CBCharacteristic * +cb_atr_characteristic(void *atr) +{ + return ((CBATTRequest *)atr).characteristic; +} + +struct byte_arr +cb_atr_value(void *atr) +{ + NSData *nsd = ((CBATTRequest *)atr).value; + return nsdata_to_byte_arr(nsd); +} + +void +cb_atr_set_value(void *atr, const struct byte_arr *ba) +{ + NSData *nsd = byte_arr_to_nsdata(ba); + ((CBATTRequest *)atr).value = nsd; +} + +int +cb_atr_offset(void *atr) +{ + return ((CBATTRequest *)atr).offset; +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/ptrmap.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/ptrmap.go new file mode 100644 index 0000000..d88f994 --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/ptrmap.go @@ -0,0 +1,41 @@ +package cbgo + +import ( + "sync" + "unsafe" +) + +// ptrMap is a thread-safe map with pointer keys and generic values. +type ptrMap struct { + m map[unsafe.Pointer]interface{} + mtx sync.RWMutex +} + +func newPtrMap() *ptrMap { + return &ptrMap{ + m: map[unsafe.Pointer]interface{}{}, + } +} + +func (pm *ptrMap) find(p unsafe.Pointer) interface{} { + pm.mtx.RLock() + defer pm.mtx.RUnlock() + + return pm.m[p] +} + +func (pm *ptrMap) add(p unsafe.Pointer, itf interface{}) error { + pm.mtx.Lock() + defer pm.mtx.Unlock() + + pm.m[p] = itf + + return nil +} + +func (pm *ptrMap) del(p unsafe.Pointer) { + pm.mtx.Lock() + defer pm.mtx.Unlock() + + delete(pm.m, p) +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/service.go b/cli/vendor/github.com/JuulLabs-OSS/cbgo/service.go new file mode 100644 index 0000000..f4c3d7f --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/service.go @@ -0,0 +1,59 @@ +package cbgo + +import "unsafe" + +/* +// See cutil.go for C compiler flags. +#import "bt.h" +*/ +import "C" + +// Service: https://developer.apple.com/documentation/corebluetooth/cbservice +type Service struct { + ptr unsafe.Pointer +} + +// UUID: https://developer.apple.com/documentation/corebluetooth/cbattribute/1620638-uuid +func (s Service) UUID() UUID { + cstr := C.cb_svc_uuid(s.ptr) + return MustParseUUID(C.GoString(cstr)) +} + +// Peripheral: https://developer.apple.com/documentation/corebluetooth/cbservice/1434334-peripheral +func (s Service) Peripheral() Peripheral { + prphPtr := C.cb_svc_peripheral(s.ptr) + return Peripheral{prphPtr} +} + +// IsPrimary: https://developer.apple.com/documentation/corebluetooth/cbservice/1434326-isprimary +func (s Service) IsPrimary() bool { + return bool(C.cb_svc_is_primary(s.ptr)) +} + +// Characteristics: https://developer.apple.com/documentation/corebluetooth/cbservice/1434319-characteristics +func (s Service) Characteristics() []Characteristic { + oa := C.cb_svc_characteristics(s.ptr) + defer C.free(unsafe.Pointer(oa.objs)) + + chrs := make([]Characteristic, oa.count) + for i, _ := range chrs { + obj := getObjArrElem(&oa, i) + chrs[i] = Characteristic{ptr: obj} + } + + return chrs +} + +// IncludedServices: https://developer.apple.com/documentation/corebluetooth/cbservice/1434324-includedservices +func (s Service) IncludedServices() []Service { + oa := C.cb_svc_included_svcs(s.ptr) + defer C.free(unsafe.Pointer(oa.objs)) + + svcs := make([]Service, oa.count) + for i, _ := range svcs { + obj := getObjArrElem(&oa, i) + svcs[i] = Service{ptr: obj} + } + + return svcs +} diff --git a/cli/vendor/github.com/JuulLabs-OSS/cbgo/util.m b/cli/vendor/github.com/JuulLabs-OSS/cbgo/util.m new file mode 100644 index 0000000..e50effb --- /dev/null +++ b/cli/vendor/github.com/JuulLabs-OSS/cbgo/util.m @@ -0,0 +1,200 @@ +#import "bt.h" + +struct byte_arr +nsdata_to_byte_arr(const NSData *nsdata) +{ + return (struct byte_arr) { + .data = [nsdata bytes], + .length = [nsdata length], + }; +} + +NSData * +byte_arr_to_nsdata(const struct byte_arr *ba) +{ + if (ba->length == 0) { + return nil; + } else { + return [NSData dataWithBytes: ba->data length: ba->length]; + } +} + +struct obj_arr +nsarray_to_obj_arr(const NSArray *arr) +{ + struct obj_arr oa = {0}; + + if (arr != nil && [arr count] > 0) { + oa.count = [arr count], + oa.objs = malloc(oa.count * sizeof (void *)), + assert(oa.objs != NULL); + + for (int i = 0; i < oa.count; i++) { + oa.objs[i] = [arr objectAtIndex:i]; + } + } + + return oa; +} + +NSArray * +obj_arr_to_nsarray(const struct obj_arr *oa) +{ + NSMutableArray *nsa = [[NSMutableArray alloc] init]; + [nsa autorelease]; + + for (int i = 0; i < oa->count; i++) { + [nsa addObject:oa->objs[i]]; + } + + return nsa; +} + +NSString * +str_to_nsstring(const char *s) +{ + return [[NSString alloc] initWithCString:s encoding:NSUTF8StringEncoding]; +} + +struct bt_error +nserror_to_bt_error(const NSError *err) +{ + if (err == NULL) { + return (struct bt_error) {0}; + } else { + return (struct bt_error) { + .msg = [err.localizedDescription UTF8String], + .code = err.code, + }; + } +} + +NSUUID * +str_to_nsuuid(const char *s) +{ + NSString *nss = str_to_nsstring(s); + return [[NSUUID alloc] initWithUUIDString:nss]; +} + +CBUUID * +str_to_cbuuid(const char *s) +{ + NSString *nss = str_to_nsstring(s); + return [CBUUID UUIDWithString:nss]; +} + +NSArray * +strs_to_nsuuids(const struct string_arr *sa) +{ + if (sa == nil || sa->count == 0) { + return nil; + }; + + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr autorelease]; + + for (int i = 0; i < sa->count; i++) { + [arr addObject:str_to_nsuuid(sa->strings[i])]; + } + + return arr; +} + +NSArray * +strs_to_cbuuids(const struct string_arr *sa) +{ + if (sa == nil || sa->count == 0) { + return nil; + }; + + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr autorelease]; + + for (int i = 0; i < sa->count; i++) { + [arr addObject:str_to_cbuuid(sa->strings[i])]; + } + + return arr; +} + +NSArray * +strs_to_nsstrings(const struct string_arr *sa) +{ + if (sa == nil || sa->count == 0) { + return nil; + }; + + NSMutableArray *arr = [[NSMutableArray alloc] init]; + [arr autorelease]; + + for (int i = 0; i < sa->count; i++) { + [arr addObject:str_to_nsstring(sa->strings[i])]; + } + + return arr; +} + +struct string_arr +cbuuids_to_strs(const NSArray *cbuuids) +{ + struct string_arr sa = {0}; + + if (cbuuids != nil && [cbuuids count] > 0) { + sa.count = [cbuuids count]; + sa.strings = malloc(sa.count * sizeof (char *)); + assert(sa.strings != NULL); + + for (int i = 0; i < sa.count; i++) { + sa.strings[i] = [[[cbuuids objectAtIndex:i] UUIDString] UTF8String]; + } + } + + return sa; +} + +int +dict_int(NSDictionary *dict, NSString *key, int dflt) +{ + NSNumber *nsn = [dict objectForKey:key]; + if (nsn == nil) { + return dflt; + } else { + return [nsn intValue]; + } +} + +const char * +dict_string(NSDictionary *dict, NSString *key) +{ + return [[dict objectForKey:key] UTF8String]; +} + +const void * +dict_data(NSDictionary *dict, NSString *key, int *out_len) +{ + NSData *data; + + data = [dict objectForKey:key]; + + *out_len = [data length]; + return [data bytes]; +} + +const struct byte_arr +dict_bytes(NSDictionary *dict, NSString *key) +{ + const NSData *nsdata = [dict objectForKey:key]; + return nsdata_to_byte_arr(nsdata); +} + +void +dict_set_bool(NSMutableDictionary *dict, NSString *key, bool val) +{ + [dict setObject:[NSNumber numberWithBool:val] forKey:key]; +} + +void +dict_set_int(NSMutableDictionary *dict, NSString *key, int val) +{ + [dict setObject:[NSNumber numberWithInt:val] forKey:key]; +} diff --git a/cli/vendor/github.com/alecthomas/template/LICENSE b/cli/vendor/github.com/alecthomas/template/LICENSE new file mode 100644 index 0000000..7448756 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cli/vendor/github.com/alecthomas/template/README.md b/cli/vendor/github.com/alecthomas/template/README.md new file mode 100644 index 0000000..ef6a8ee --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/README.md @@ -0,0 +1,25 @@ +# Go's `text/template` package with newline elision + +This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline. + +eg. + +``` +{{if true}}\ +hello +{{end}}\ +``` + +Will result in: + +``` +hello\n +``` + +Rather than: + +``` +\n +hello\n +\n +``` diff --git a/cli/vendor/github.com/alecthomas/template/doc.go b/cli/vendor/github.com/alecthomas/template/doc.go new file mode 100644 index 0000000..223c595 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/doc.go @@ -0,0 +1,406 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package template implements data-driven templates for generating textual output. + +To generate HTML output, see package html/template, which has the same interface +as this package but automatically secures HTML output against certain attacks. + +Templates are executed by applying them to a data structure. Annotations in the +template refer to elements of the data structure (typically a field of a struct +or a key in a map) to control execution and derive values to be displayed. +Execution of the template walks the structure and sets the cursor, represented +by a period '.' and called "dot", to the value at the current location in the +structure as execution proceeds. + +The input text for a template is UTF-8-encoded text in any format. +"Actions"--data evaluations or control structures--are delimited by +"{{" and "}}"; all text outside actions is copied to the output unchanged. +Actions may not span newlines, although comments can. + +Once parsed, a template may be executed safely in parallel. + +Here is a trivial example that prints "17 items are made of wool". + + type Inventory struct { + Material string + Count uint + } + sweaters := Inventory{"wool", 17} + tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") + if err != nil { panic(err) } + err = tmpl.Execute(os.Stdout, sweaters) + if err != nil { panic(err) } + +More intricate examples appear below. + +Actions + +Here is the list of actions. "Arguments" and "pipelines" are evaluations of +data, defined in detail below. + +*/ +// {{/* a comment */}} +// A comment; discarded. May contain newlines. +// Comments do not nest and must start and end at the +// delimiters, as shown here. +/* + + {{pipeline}} + The default textual representation of the value of the pipeline + is copied to the output. + + {{if pipeline}} T1 {{end}} + If the value of the pipeline is empty, no output is generated; + otherwise, T1 is executed. The empty values are false, 0, any + nil pointer or interface value, and any array, slice, map, or + string of length zero. + Dot is unaffected. + + {{if pipeline}} T1 {{else}} T0 {{end}} + If the value of the pipeline is empty, T0 is executed; + otherwise, T1 is executed. Dot is unaffected. + + {{if pipeline}} T1 {{else if pipeline}} T0 {{end}} + To simplify the appearance of if-else chains, the else action + of an if may include another if directly; the effect is exactly + the same as writing + {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}} + + {{range pipeline}} T1 {{end}} + The value of the pipeline must be an array, slice, map, or channel. + If the value of the pipeline has length zero, nothing is output; + otherwise, dot is set to the successive elements of the array, + slice, or map and T1 is executed. If the value is a map and the + keys are of basic type with a defined order ("comparable"), the + elements will be visited in sorted key order. + + {{range pipeline}} T1 {{else}} T0 {{end}} + The value of the pipeline must be an array, slice, map, or channel. + If the value of the pipeline has length zero, dot is unaffected and + T0 is executed; otherwise, dot is set to the successive elements + of the array, slice, or map and T1 is executed. + + {{template "name"}} + The template with the specified name is executed with nil data. + + {{template "name" pipeline}} + The template with the specified name is executed with dot set + to the value of the pipeline. + + {{with pipeline}} T1 {{end}} + If the value of the pipeline is empty, no output is generated; + otherwise, dot is set to the value of the pipeline and T1 is + executed. + + {{with pipeline}} T1 {{else}} T0 {{end}} + If the value of the pipeline is empty, dot is unaffected and T0 + is executed; otherwise, dot is set to the value of the pipeline + and T1 is executed. + +Arguments + +An argument is a simple value, denoted by one of the following. + + - A boolean, string, character, integer, floating-point, imaginary + or complex constant in Go syntax. These behave like Go's untyped + constants, although raw strings may not span newlines. + - The keyword nil, representing an untyped Go nil. + - The character '.' (period): + . + The result is the value of dot. + - A variable name, which is a (possibly empty) alphanumeric string + preceded by a dollar sign, such as + $piOver2 + or + $ + The result is the value of the variable. + Variables are described below. + - The name of a field of the data, which must be a struct, preceded + by a period, such as + .Field + The result is the value of the field. Field invocations may be + chained: + .Field1.Field2 + Fields can also be evaluated on variables, including chaining: + $x.Field1.Field2 + - The name of a key of the data, which must be a map, preceded + by a period, such as + .Key + The result is the map element value indexed by the key. + Key invocations may be chained and combined with fields to any + depth: + .Field1.Key1.Field2.Key2 + Although the key must be an alphanumeric identifier, unlike with + field names they do not need to start with an upper case letter. + Keys can also be evaluated on variables, including chaining: + $x.key1.key2 + - The name of a niladic method of the data, preceded by a period, + such as + .Method + The result is the value of invoking the method with dot as the + receiver, dot.Method(). Such a method must have one return value (of + any type) or two return values, the second of which is an error. + If it has two and the returned error is non-nil, execution terminates + and an error is returned to the caller as the value of Execute. + Method invocations may be chained and combined with fields and keys + to any depth: + .Field1.Key1.Method1.Field2.Key2.Method2 + Methods can also be evaluated on variables, including chaining: + $x.Method1.Field + - The name of a niladic function, such as + fun + The result is the value of invoking the function, fun(). The return + types and values behave as in methods. Functions and function + names are described below. + - A parenthesized instance of one the above, for grouping. The result + may be accessed by a field or map key invocation. + print (.F1 arg1) (.F2 arg2) + (.StructValuedMethod "arg").Field + +Arguments may evaluate to any type; if they are pointers the implementation +automatically indirects to the base type when required. +If an evaluation yields a function value, such as a function-valued +field of a struct, the function is not invoked automatically, but it +can be used as a truth value for an if action and the like. To invoke +it, use the call function, defined below. + +A pipeline is a possibly chained sequence of "commands". A command is a simple +value (argument) or a function or method call, possibly with multiple arguments: + + Argument + The result is the value of evaluating the argument. + .Method [Argument...] + The method can be alone or the last element of a chain but, + unlike methods in the middle of a chain, it can take arguments. + The result is the value of calling the method with the + arguments: + dot.Method(Argument1, etc.) + functionName [Argument...] + The result is the value of calling the function associated + with the name: + function(Argument1, etc.) + Functions and function names are described below. + +Pipelines + +A pipeline may be "chained" by separating a sequence of commands with pipeline +characters '|'. In a chained pipeline, the result of the each command is +passed as the last argument of the following command. The output of the final +command in the pipeline is the value of the pipeline. + +The output of a command will be either one value or two values, the second of +which has type error. If that second value is present and evaluates to +non-nil, execution terminates and the error is returned to the caller of +Execute. + +Variables + +A pipeline inside an action may initialize a variable to capture the result. +The initialization has syntax + + $variable := pipeline + +where $variable is the name of the variable. An action that declares a +variable produces no output. + +If a "range" action initializes a variable, the variable is set to the +successive elements of the iteration. Also, a "range" may declare two +variables, separated by a comma: + + range $index, $element := pipeline + +in which case $index and $element are set to the successive values of the +array/slice index or map key and element, respectively. Note that if there is +only one variable, it is assigned the element; this is opposite to the +convention in Go range clauses. + +A variable's scope extends to the "end" action of the control structure ("if", +"with", or "range") in which it is declared, or to the end of the template if +there is no such control structure. A template invocation does not inherit +variables from the point of its invocation. + +When execution begins, $ is set to the data argument passed to Execute, that is, +to the starting value of dot. + +Examples + +Here are some example one-line templates demonstrating pipelines and variables. +All produce the quoted word "output": + + {{"\"output\""}} + A string constant. + {{`"output"`}} + A raw string constant. + {{printf "%q" "output"}} + A function call. + {{"output" | printf "%q"}} + A function call whose final argument comes from the previous + command. + {{printf "%q" (print "out" "put")}} + A parenthesized argument. + {{"put" | printf "%s%s" "out" | printf "%q"}} + A more elaborate call. + {{"output" | printf "%s" | printf "%q"}} + A longer chain. + {{with "output"}}{{printf "%q" .}}{{end}} + A with action using dot. + {{with $x := "output" | printf "%q"}}{{$x}}{{end}} + A with action that creates and uses a variable. + {{with $x := "output"}}{{printf "%q" $x}}{{end}} + A with action that uses the variable in another action. + {{with $x := "output"}}{{$x | printf "%q"}}{{end}} + The same, but pipelined. + +Functions + +During execution functions are found in two function maps: first in the +template, then in the global function map. By default, no functions are defined +in the template but the Funcs method can be used to add them. + +Predefined global functions are named as follows. + + and + Returns the boolean AND of its arguments by returning the + first empty argument or the last argument, that is, + "and x y" behaves as "if x then y else x". All the + arguments are evaluated. + call + Returns the result of calling the first argument, which + must be a function, with the remaining arguments as parameters. + Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where + Y is a func-valued field, map entry, or the like. + The first argument must be the result of an evaluation + that yields a value of function type (as distinct from + a predefined function such as print). The function must + return either one or two result values, the second of which + is of type error. If the arguments don't match the function + or the returned error value is non-nil, execution stops. + html + Returns the escaped HTML equivalent of the textual + representation of its arguments. + index + Returns the result of indexing its first argument by the + following arguments. Thus "index x 1 2 3" is, in Go syntax, + x[1][2][3]. Each indexed item must be a map, slice, or array. + js + Returns the escaped JavaScript equivalent of the textual + representation of its arguments. + len + Returns the integer length of its argument. + not + Returns the boolean negation of its single argument. + or + Returns the boolean OR of its arguments by returning the + first non-empty argument or the last argument, that is, + "or x y" behaves as "if x then x else y". All the + arguments are evaluated. + print + An alias for fmt.Sprint + printf + An alias for fmt.Sprintf + println + An alias for fmt.Sprintln + urlquery + Returns the escaped value of the textual representation of + its arguments in a form suitable for embedding in a URL query. + +The boolean functions take any zero value to be false and a non-zero +value to be true. + +There is also a set of binary comparison operators defined as +functions: + + eq + Returns the boolean truth of arg1 == arg2 + ne + Returns the boolean truth of arg1 != arg2 + lt + Returns the boolean truth of arg1 < arg2 + le + Returns the boolean truth of arg1 <= arg2 + gt + Returns the boolean truth of arg1 > arg2 + ge + Returns the boolean truth of arg1 >= arg2 + +For simpler multi-way equality tests, eq (only) accepts two or more +arguments and compares the second and subsequent to the first, +returning in effect + + arg1==arg2 || arg1==arg3 || arg1==arg4 ... + +(Unlike with || in Go, however, eq is a function call and all the +arguments will be evaluated.) + +The comparison functions work on basic types only (or named basic +types, such as "type Celsius float32"). They implement the Go rules +for comparison of values, except that size and exact type are +ignored, so any integer value, signed or unsigned, may be compared +with any other integer value. (The arithmetic value is compared, +not the bit pattern, so all negative integers are less than all +unsigned integers.) However, as usual, one may not compare an int +with a float32 and so on. + +Associated templates + +Each template is named by a string specified when it is created. Also, each +template is associated with zero or more other templates that it may invoke by +name; such associations are transitive and form a name space of templates. + +A template may use a template invocation to instantiate another associated +template; see the explanation of the "template" action above. The name must be +that of a template associated with the template that contains the invocation. + +Nested template definitions + +When parsing a template, another template may be defined and associated with the +template being parsed. Template definitions must appear at the top level of the +template, much like global variables in a Go program. + +The syntax of such definitions is to surround each template declaration with a +"define" and "end" action. + +The define action names the template being created by providing a string +constant. Here is a simple example: + + `{{define "T1"}}ONE{{end}} + {{define "T2"}}TWO{{end}} + {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}} + {{template "T3"}}` + +This defines two templates, T1 and T2, and a third T3 that invokes the other two +when it is executed. Finally it invokes T3. If executed this template will +produce the text + + ONE TWO + +By construction, a template may reside in only one association. If it's +necessary to have a template addressable from multiple associations, the +template definition must be parsed multiple times to create distinct *Template +values, or must be copied with the Clone or AddParseTree method. + +Parse may be called multiple times to assemble the various associated templates; +see the ParseFiles and ParseGlob functions and methods for simple ways to parse +related templates stored in files. + +A template may be executed directly or through ExecuteTemplate, which executes +an associated template identified by name. To invoke our example above, we +might write, + + err := tmpl.Execute(os.Stdout, "no data needed") + if err != nil { + log.Fatalf("execution failed: %s", err) + } + +or to invoke a particular template explicitly by name, + + err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed") + if err != nil { + log.Fatalf("execution failed: %s", err) + } + +*/ +package template diff --git a/cli/vendor/github.com/alecthomas/template/exec.go b/cli/vendor/github.com/alecthomas/template/exec.go new file mode 100644 index 0000000..c3078e5 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/exec.go @@ -0,0 +1,845 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "fmt" + "io" + "reflect" + "runtime" + "sort" + "strings" + + "github.com/alecthomas/template/parse" +) + +// state represents the state of an execution. It's not part of the +// template so that multiple executions of the same template +// can execute in parallel. +type state struct { + tmpl *Template + wr io.Writer + node parse.Node // current node, for errors + vars []variable // push-down stack of variable values. +} + +// variable holds the dynamic value of a variable such as $, $x etc. +type variable struct { + name string + value reflect.Value +} + +// push pushes a new variable on the stack. +func (s *state) push(name string, value reflect.Value) { + s.vars = append(s.vars, variable{name, value}) +} + +// mark returns the length of the variable stack. +func (s *state) mark() int { + return len(s.vars) +} + +// pop pops the variable stack up to the mark. +func (s *state) pop(mark int) { + s.vars = s.vars[0:mark] +} + +// setVar overwrites the top-nth variable on the stack. Used by range iterations. +func (s *state) setVar(n int, value reflect.Value) { + s.vars[len(s.vars)-n].value = value +} + +// varValue returns the value of the named variable. +func (s *state) varValue(name string) reflect.Value { + for i := s.mark() - 1; i >= 0; i-- { + if s.vars[i].name == name { + return s.vars[i].value + } + } + s.errorf("undefined variable: %s", name) + return zero +} + +var zero reflect.Value + +// at marks the state to be on node n, for error reporting. +func (s *state) at(node parse.Node) { + s.node = node +} + +// doublePercent returns the string with %'s replaced by %%, if necessary, +// so it can be used safely inside a Printf format string. +func doublePercent(str string) string { + if strings.Contains(str, "%") { + str = strings.Replace(str, "%", "%%", -1) + } + return str +} + +// errorf formats the error and terminates processing. +func (s *state) errorf(format string, args ...interface{}) { + name := doublePercent(s.tmpl.Name()) + if s.node == nil { + format = fmt.Sprintf("template: %s: %s", name, format) + } else { + location, context := s.tmpl.ErrorContext(s.node) + format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format) + } + panic(fmt.Errorf(format, args...)) +} + +// errRecover is the handler that turns panics into returns from the top +// level of Parse. +func errRecover(errp *error) { + e := recover() + if e != nil { + switch err := e.(type) { + case runtime.Error: + panic(e) + case error: + *errp = err + default: + panic(e) + } + } +} + +// ExecuteTemplate applies the template associated with t that has the given name +// to the specified data object and writes the output to wr. +// If an error occurs executing the template or writing its output, +// execution stops, but partial results may already have been written to +// the output writer. +// A template may be executed safely in parallel. +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { + tmpl := t.tmpl[name] + if tmpl == nil { + return fmt.Errorf("template: no template %q associated with template %q", name, t.name) + } + return tmpl.Execute(wr, data) +} + +// Execute applies a parsed template to the specified data object, +// and writes the output to wr. +// If an error occurs executing the template or writing its output, +// execution stops, but partial results may already have been written to +// the output writer. +// A template may be executed safely in parallel. +func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { + defer errRecover(&err) + value := reflect.ValueOf(data) + state := &state{ + tmpl: t, + wr: wr, + vars: []variable{{"$", value}}, + } + t.init() + if t.Tree == nil || t.Root == nil { + var b bytes.Buffer + for name, tmpl := range t.tmpl { + if tmpl.Tree == nil || tmpl.Root == nil { + continue + } + if b.Len() > 0 { + b.WriteString(", ") + } + fmt.Fprintf(&b, "%q", name) + } + var s string + if b.Len() > 0 { + s = "; defined templates are: " + b.String() + } + state.errorf("%q is an incomplete or empty template%s", t.Name(), s) + } + state.walk(value, t.Root) + return +} + +// Walk functions step through the major pieces of the template structure, +// generating output as they go. +func (s *state) walk(dot reflect.Value, node parse.Node) { + s.at(node) + switch node := node.(type) { + case *parse.ActionNode: + // Do not pop variables so they persist until next end. + // Also, if the action declares variables, don't print the result. + val := s.evalPipeline(dot, node.Pipe) + if len(node.Pipe.Decl) == 0 { + s.printValue(node, val) + } + case *parse.IfNode: + s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) + case *parse.ListNode: + for _, node := range node.Nodes { + s.walk(dot, node) + } + case *parse.RangeNode: + s.walkRange(dot, node) + case *parse.TemplateNode: + s.walkTemplate(dot, node) + case *parse.TextNode: + if _, err := s.wr.Write(node.Text); err != nil { + s.errorf("%s", err) + } + case *parse.WithNode: + s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList) + default: + s.errorf("unknown node: %s", node) + } +} + +// walkIfOrWith walks an 'if' or 'with' node. The two control structures +// are identical in behavior except that 'with' sets dot. +func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) { + defer s.pop(s.mark()) + val := s.evalPipeline(dot, pipe) + truth, ok := isTrue(val) + if !ok { + s.errorf("if/with can't use %v", val) + } + if truth { + if typ == parse.NodeWith { + s.walk(val, list) + } else { + s.walk(dot, list) + } + } else if elseList != nil { + s.walk(dot, elseList) + } +} + +// isTrue reports whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. +func isTrue(val reflect.Value) (truth, ok bool) { + if !val.IsValid() { + // Something like var x interface{}, never set. It's a form of nil. + return false, true + } + switch val.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + truth = val.Len() > 0 + case reflect.Bool: + truth = val.Bool() + case reflect.Complex64, reflect.Complex128: + truth = val.Complex() != 0 + case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: + truth = !val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + truth = val.Int() != 0 + case reflect.Float32, reflect.Float64: + truth = val.Float() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + truth = val.Uint() != 0 + case reflect.Struct: + truth = true // Struct values are always true. + default: + return + } + return truth, true +} + +func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { + s.at(r) + defer s.pop(s.mark()) + val, _ := indirect(s.evalPipeline(dot, r.Pipe)) + // mark top of stack before any variables in the body are pushed. + mark := s.mark() + oneIteration := func(index, elem reflect.Value) { + // Set top var (lexically the second if there are two) to the element. + if len(r.Pipe.Decl) > 0 { + s.setVar(1, elem) + } + // Set next var (lexically the first if there are two) to the index. + if len(r.Pipe.Decl) > 1 { + s.setVar(2, index) + } + s.walk(elem, r.List) + s.pop(mark) + } + switch val.Kind() { + case reflect.Array, reflect.Slice: + if val.Len() == 0 { + break + } + for i := 0; i < val.Len(); i++ { + oneIteration(reflect.ValueOf(i), val.Index(i)) + } + return + case reflect.Map: + if val.Len() == 0 { + break + } + for _, key := range sortKeys(val.MapKeys()) { + oneIteration(key, val.MapIndex(key)) + } + return + case reflect.Chan: + if val.IsNil() { + break + } + i := 0 + for ; ; i++ { + elem, ok := val.Recv() + if !ok { + break + } + oneIteration(reflect.ValueOf(i), elem) + } + if i == 0 { + break + } + return + case reflect.Invalid: + break // An invalid value is likely a nil map, etc. and acts like an empty map. + default: + s.errorf("range can't iterate over %v", val) + } + if r.ElseList != nil { + s.walk(dot, r.ElseList) + } +} + +func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { + s.at(t) + tmpl := s.tmpl.tmpl[t.Name] + if tmpl == nil { + s.errorf("template %q not defined", t.Name) + } + // Variables declared by the pipeline persist. + dot = s.evalPipeline(dot, t.Pipe) + newState := *s + newState.tmpl = tmpl + // No dynamic scoping: template invocations inherit no variables. + newState.vars = []variable{{"$", dot}} + newState.walk(dot, tmpl.Root) +} + +// Eval functions evaluate pipelines, commands, and their elements and extract +// values from the data structure by examining fields, calling methods, and so on. +// The printing of those values happens only through walk functions. + +// evalPipeline returns the value acquired by evaluating a pipeline. If the +// pipeline has a variable declaration, the variable will be pushed on the +// stack. Callers should therefore pop the stack after they are finished +// executing commands depending on the pipeline value. +func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) { + if pipe == nil { + return + } + s.at(pipe) + for _, cmd := range pipe.Cmds { + value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. + // If the object has type interface{}, dig down one level to the thing inside. + if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { + value = reflect.ValueOf(value.Interface()) // lovely! + } + } + for _, variable := range pipe.Decl { + s.push(variable.Ident[0], value) + } + return value +} + +func (s *state) notAFunction(args []parse.Node, final reflect.Value) { + if len(args) > 1 || final.IsValid() { + s.errorf("can't give argument to non-function %s", args[0]) + } +} + +func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value { + firstWord := cmd.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + return s.evalFieldNode(dot, n, cmd.Args, final) + case *parse.ChainNode: + return s.evalChainNode(dot, n, cmd.Args, final) + case *parse.IdentifierNode: + // Must be a function. + return s.evalFunction(dot, n, cmd, cmd.Args, final) + case *parse.PipeNode: + // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored. + return s.evalPipeline(dot, n) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, cmd.Args, final) + } + s.at(firstWord) + s.notAFunction(cmd.Args, final) + switch word := firstWord.(type) { + case *parse.BoolNode: + return reflect.ValueOf(word.True) + case *parse.DotNode: + return dot + case *parse.NilNode: + s.errorf("nil is not a command") + case *parse.NumberNode: + return s.idealConstant(word) + case *parse.StringNode: + return reflect.ValueOf(word.Text) + } + s.errorf("can't evaluate command %q", firstWord) + panic("not reached") +} + +// idealConstant is called to return the value of a number in a context where +// we don't know the type. In that case, the syntax of the number tells us +// its type, and we use Go rules to resolve. Note there is no such thing as +// a uint ideal constant in this situation - the value must be of int type. +func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { + // These are ideal constants but we don't know the type + // and we have no context. (If it was a method argument, + // we'd know what we need.) The syntax guides us to some extent. + s.at(constant) + switch { + case constant.IsComplex: + return reflect.ValueOf(constant.Complex128) // incontrovertible. + case constant.IsFloat && !isHexConstant(constant.Text) && strings.IndexAny(constant.Text, ".eE") >= 0: + return reflect.ValueOf(constant.Float64) + case constant.IsInt: + n := int(constant.Int64) + if int64(n) != constant.Int64 { + s.errorf("%s overflows int", constant.Text) + } + return reflect.ValueOf(n) + case constant.IsUint: + s.errorf("%s overflows int", constant.Text) + } + return zero +} + +func isHexConstant(s string) bool { + return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') +} + +func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { + s.at(field) + return s.evalFieldChain(dot, dot, field, field.Ident, args, final) +} + +func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value { + s.at(chain) + // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields. + pipe := s.evalArg(dot, nil, chain.Node) + if len(chain.Field) == 0 { + s.errorf("internal error: no fields in evalChainNode") + } + return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final) +} + +func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { + // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields. + s.at(variable) + value := s.varValue(variable.Ident[0]) + if len(variable.Ident) == 1 { + s.notAFunction(args, final) + return value + } + return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final) +} + +// evalFieldChain evaluates .X.Y.Z possibly followed by arguments. +// dot is the environment in which to evaluate arguments, while +// receiver is the value being walked along the chain. +func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value { + n := len(ident) + for i := 0; i < n-1; i++ { + receiver = s.evalField(dot, ident[i], node, nil, zero, receiver) + } + // Now if it's a method, it gets the arguments. + return s.evalField(dot, ident[n-1], node, args, final, receiver) +} + +func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { + s.at(node) + name := node.Ident + function, ok := findFunction(name, s.tmpl) + if !ok { + s.errorf("%q is not a defined function", name) + } + return s.evalCall(dot, function, cmd, name, args, final) +} + +// evalField evaluates an expression like (.Field) or (.Field arg1 arg2). +// The 'final' argument represents the return value from the preceding +// value of the pipeline, if any. +func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { + if !receiver.IsValid() { + return zero + } + typ := receiver.Type() + receiver, _ = indirect(receiver) + // Unless it's an interface, need to get to a value of type *T to guarantee + // we see all methods of T and *T. + ptr := receiver + if ptr.Kind() != reflect.Interface && ptr.CanAddr() { + ptr = ptr.Addr() + } + if method := ptr.MethodByName(fieldName); method.IsValid() { + return s.evalCall(dot, method, node, fieldName, args, final) + } + hasArgs := len(args) > 1 || final.IsValid() + // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil. + receiver, isNil := indirect(receiver) + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } + switch receiver.Kind() { + case reflect.Struct: + tField, ok := receiver.Type().FieldByName(fieldName) + if ok { + field := receiver.FieldByIndex(tField.Index) + if tField.PkgPath != "" { // field is unexported + s.errorf("%s is an unexported field of struct type %s", fieldName, typ) + } + // If it's a function, we must call it. + if hasArgs { + s.errorf("%s has arguments but cannot be invoked as function", fieldName) + } + return field + } + s.errorf("%s is not a field of struct type %s", fieldName, typ) + case reflect.Map: + // If it's a map, attempt to use the field name as a key. + nameVal := reflect.ValueOf(fieldName) + if nameVal.Type().AssignableTo(receiver.Type().Key()) { + if hasArgs { + s.errorf("%s is not a method but has arguments", fieldName) + } + return receiver.MapIndex(nameVal) + } + } + s.errorf("can't evaluate field %s in type %s", fieldName, typ) + panic("not reached") +} + +var ( + errorType = reflect.TypeOf((*error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() +) + +// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so +// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] +// as the function itself. +func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { + if args != nil { + args = args[1:] // Zeroth arg is function name/node; not passed to function. + } + typ := fun.Type() + numIn := len(args) + if final.IsValid() { + numIn++ + } + numFixed := len(args) + if typ.IsVariadic() { + numFixed = typ.NumIn() - 1 // last arg is the variadic one. + if numIn < numFixed { + s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) + } + } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { + s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) + } + if !goodFunc(typ) { + // TODO: This could still be a confusing error; maybe goodFunc should provide info. + s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) + } + // Build the arg list. + argv := make([]reflect.Value, numIn) + // Args must be evaluated. Fixed args first. + i := 0 + for ; i < numFixed && i < len(args); i++ { + argv[i] = s.evalArg(dot, typ.In(i), args[i]) + } + // Now the ... args. + if typ.IsVariadic() { + argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. + for ; i < len(args); i++ { + argv[i] = s.evalArg(dot, argType, args[i]) + } + } + // Add final value if necessary. + if final.IsValid() { + t := typ.In(typ.NumIn() - 1) + if typ.IsVariadic() { + t = t.Elem() + } + argv[i] = s.validateType(final, t) + } + result := fun.Call(argv) + // If we have an error that is not nil, stop execution and return that error to the caller. + if len(result) == 2 && !result[1].IsNil() { + s.at(node) + s.errorf("error calling %s: %s", name, result[1].Interface().(error)) + } + return result[0] +} + +// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. +func canBeNil(typ reflect.Type) bool { + switch typ.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return true + } + return false +} + +// validateType guarantees that the value is valid and assignable to the type. +func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { + if !value.IsValid() { + if typ == nil || canBeNil(typ) { + // An untyped nil interface{}. Accept as a proper nil value. + return reflect.Zero(typ) + } + s.errorf("invalid value; expected %s", typ) + } + if typ != nil && !value.Type().AssignableTo(typ) { + if value.Kind() == reflect.Interface && !value.IsNil() { + value = value.Elem() + if value.Type().AssignableTo(typ) { + return value + } + // fallthrough + } + // Does one dereference or indirection work? We could do more, as we + // do with method receivers, but that gets messy and method receivers + // are much more constrained, so it makes more sense there than here. + // Besides, one is almost always all you need. + switch { + case value.Kind() == reflect.Ptr && value.Type().Elem().AssignableTo(typ): + value = value.Elem() + if !value.IsValid() { + s.errorf("dereference of nil pointer of type %s", typ) + } + case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr(): + value = value.Addr() + default: + s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) + } + } + return value +} + +func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + switch arg := n.(type) { + case *parse.DotNode: + return s.validateType(dot, typ) + case *parse.NilNode: + if canBeNil(typ) { + return reflect.Zero(typ) + } + s.errorf("cannot assign nil to %s", typ) + case *parse.FieldNode: + return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, zero), typ) + case *parse.VariableNode: + return s.validateType(s.evalVariableNode(dot, arg, nil, zero), typ) + case *parse.PipeNode: + return s.validateType(s.evalPipeline(dot, arg), typ) + case *parse.IdentifierNode: + return s.evalFunction(dot, arg, arg, nil, zero) + case *parse.ChainNode: + return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ) + } + switch typ.Kind() { + case reflect.Bool: + return s.evalBool(typ, n) + case reflect.Complex64, reflect.Complex128: + return s.evalComplex(typ, n) + case reflect.Float32, reflect.Float64: + return s.evalFloat(typ, n) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return s.evalInteger(typ, n) + case reflect.Interface: + if typ.NumMethod() == 0 { + return s.evalEmptyInterface(dot, n) + } + case reflect.String: + return s.evalString(typ, n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return s.evalUnsignedInteger(typ, n) + } + s.errorf("can't handle %s for arg of type %s", n, typ) + panic("not reached") +} + +func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.BoolNode); ok { + value := reflect.New(typ).Elem() + value.SetBool(n.True) + return value + } + s.errorf("expected bool; found %s", n) + panic("not reached") +} + +func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.StringNode); ok { + value := reflect.New(typ).Elem() + value.SetString(n.Text) + return value + } + s.errorf("expected string; found %s", n) + panic("not reached") +} + +func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsInt { + value := reflect.New(typ).Elem() + value.SetInt(n.Int64) + return value + } + s.errorf("expected integer; found %s", n) + panic("not reached") +} + +func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsUint { + value := reflect.New(typ).Elem() + value.SetUint(n.Uint64) + return value + } + s.errorf("expected unsigned integer; found %s", n) + panic("not reached") +} + +func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsFloat { + value := reflect.New(typ).Elem() + value.SetFloat(n.Float64) + return value + } + s.errorf("expected float; found %s", n) + panic("not reached") +} + +func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsComplex { + value := reflect.New(typ).Elem() + value.SetComplex(n.Complex128) + return value + } + s.errorf("expected complex; found %s", n) + panic("not reached") +} + +func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value { + s.at(n) + switch n := n.(type) { + case *parse.BoolNode: + return reflect.ValueOf(n.True) + case *parse.DotNode: + return dot + case *parse.FieldNode: + return s.evalFieldNode(dot, n, nil, zero) + case *parse.IdentifierNode: + return s.evalFunction(dot, n, n, nil, zero) + case *parse.NilNode: + // NilNode is handled in evalArg, the only place that calls here. + s.errorf("evalEmptyInterface: nil (can't happen)") + case *parse.NumberNode: + return s.idealConstant(n) + case *parse.StringNode: + return reflect.ValueOf(n.Text) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, nil, zero) + case *parse.PipeNode: + return s.evalPipeline(dot, n) + } + s.errorf("can't handle assignment of %s to empty interface argument", n) + panic("not reached") +} + +// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. +// We indirect through pointers and empty interfaces (only) because +// non-empty interfaces have methods we might need. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { + if v.IsNil() { + return v, true + } + if v.Kind() == reflect.Interface && v.NumMethod() > 0 { + break + } + } + return v, false +} + +// printValue writes the textual representation of the value to the output of +// the template. +func (s *state) printValue(n parse.Node, v reflect.Value) { + s.at(n) + iface, ok := printableValue(v) + if !ok { + s.errorf("can't print %s of type %s", n, v.Type()) + } + fmt.Fprint(s.wr, iface) +} + +// printableValue returns the, possibly indirected, interface value inside v that +// is best for a call to formatted printer. +func printableValue(v reflect.Value) (interface{}, bool) { + if v.Kind() == reflect.Ptr { + v, _ = indirect(v) // fmt.Fprint handles nil. + } + if !v.IsValid() { + return "", true + } + + if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { + if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) { + v = v.Addr() + } else { + switch v.Kind() { + case reflect.Chan, reflect.Func: + return nil, false + } + } + } + return v.Interface(), true +} + +// Types to help sort the keys in a map for reproducible output. + +type rvs []reflect.Value + +func (x rvs) Len() int { return len(x) } +func (x rvs) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +type rvInts struct{ rvs } + +func (x rvInts) Less(i, j int) bool { return x.rvs[i].Int() < x.rvs[j].Int() } + +type rvUints struct{ rvs } + +func (x rvUints) Less(i, j int) bool { return x.rvs[i].Uint() < x.rvs[j].Uint() } + +type rvFloats struct{ rvs } + +func (x rvFloats) Less(i, j int) bool { return x.rvs[i].Float() < x.rvs[j].Float() } + +type rvStrings struct{ rvs } + +func (x rvStrings) Less(i, j int) bool { return x.rvs[i].String() < x.rvs[j].String() } + +// sortKeys sorts (if it can) the slice of reflect.Values, which is a slice of map keys. +func sortKeys(v []reflect.Value) []reflect.Value { + if len(v) <= 1 { + return v + } + switch v[0].Kind() { + case reflect.Float32, reflect.Float64: + sort.Sort(rvFloats{v}) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + sort.Sort(rvInts{v}) + case reflect.String: + sort.Sort(rvStrings{v}) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + sort.Sort(rvUints{v}) + } + return v +} diff --git a/cli/vendor/github.com/alecthomas/template/funcs.go b/cli/vendor/github.com/alecthomas/template/funcs.go new file mode 100644 index 0000000..39ee5ed --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/funcs.go @@ -0,0 +1,598 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/url" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +// FuncMap is the type of the map defining the mapping from names to functions. +// Each function must have either a single return value, or two return values of +// which the second has type error. In that case, if the second (error) +// return value evaluates to non-nil during execution, execution terminates and +// Execute returns that error. +type FuncMap map[string]interface{} + +var builtins = FuncMap{ + "and": and, + "call": call, + "html": HTMLEscaper, + "index": index, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, + + // Comparisons + "eq": eq, // == + "ge": ge, // >= + "gt": gt, // > + "le": le, // <= + "lt": lt, // < + "ne": ne, // != +} + +var builtinFuncs = createValueFuncs(builtins) + +// createValueFuncs turns a FuncMap into a map[string]reflect.Value +func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { + m := make(map[string]reflect.Value) + addValueFuncs(m, funcMap) + return m +} + +// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. +func addValueFuncs(out map[string]reflect.Value, in FuncMap) { + for name, fn := range in { + v := reflect.ValueOf(fn) + if v.Kind() != reflect.Func { + panic("value for " + name + " not a function") + } + if !goodFunc(v.Type()) { + panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut())) + } + out[name] = v + } +} + +// addFuncs adds to values the functions in funcs. It does no checking of the input - +// call addValueFuncs first. +func addFuncs(out, in FuncMap) { + for name, fn := range in { + out[name] = fn + } +} + +// goodFunc checks that the function or method has the right result signature. +func goodFunc(typ reflect.Type) bool { + // We allow functions with 1 result or 2 results where the second is an error. + switch { + case typ.NumOut() == 1: + return true + case typ.NumOut() == 2 && typ.Out(1) == errorType: + return true + } + return false +} + +// findFunction looks for a function in the template, and global map. +func findFunction(name string, tmpl *Template) (reflect.Value, bool) { + if tmpl != nil && tmpl.common != nil { + if fn := tmpl.execFuncs[name]; fn.IsValid() { + return fn, true + } + } + if fn := builtinFuncs[name]; fn.IsValid() { + return fn, true + } + return reflect.Value{}, false +} + +// Indexing. + +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func index(item interface{}, indices ...interface{}) (interface{}, error) { + v := reflect.ValueOf(item) + for _, i := range indices { + index := reflect.ValueOf(i) + var isNil bool + if v, isNil = indirect(v); isNil { + return nil, fmt.Errorf("index of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.String: + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + default: + return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + } + if x < 0 || x >= int64(v.Len()) { + return nil, fmt.Errorf("index out of range: %d", x) + } + v = v.Index(int(x)) + case reflect.Map: + if !index.IsValid() { + index = reflect.Zero(v.Type().Key()) + } + if !index.Type().AssignableTo(v.Type().Key()) { + return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) + } + if x := v.MapIndex(index); x.IsValid() { + v = x + } else { + v = reflect.Zero(v.Type().Elem()) + } + default: + return nil, fmt.Errorf("can't index item of type %s", v.Type()) + } + } + return v.Interface(), nil +} + +// Length + +// length returns the length of the item, with an error if it has no defined length. +func length(item interface{}) (int, error) { + v, isNil := indirect(reflect.ValueOf(item)) + if isNil { + return 0, fmt.Errorf("len of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len(), nil + } + return 0, fmt.Errorf("len of type %s", v.Type()) +} + +// Function invocation + +// call returns the result of evaluating the first argument as a function. +// The function must return 1 result, or 2 results, the second of which is an error. +func call(fn interface{}, args ...interface{}) (interface{}, error) { + v := reflect.ValueOf(fn) + typ := v.Type() + if typ.Kind() != reflect.Func { + return nil, fmt.Errorf("non-function of type %s", typ) + } + if !goodFunc(typ) { + return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) + } + numIn := typ.NumIn() + var dddType reflect.Type + if typ.IsVariadic() { + if len(args) < numIn-1 { + return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) + } + dddType = typ.In(numIn - 1).Elem() + } else { + if len(args) != numIn { + return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) + } + } + argv := make([]reflect.Value, len(args)) + for i, arg := range args { + value := reflect.ValueOf(arg) + // Compute the expected type. Clumsy because of variadics. + var argType reflect.Type + if !typ.IsVariadic() || i < numIn-1 { + argType = typ.In(i) + } else { + argType = dddType + } + if !value.IsValid() && canBeNil(argType) { + value = reflect.Zero(argType) + } + if !value.Type().AssignableTo(argType) { + return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) + } + argv[i] = value + } + result := v.Call(argv) + if len(result) == 2 && !result[1].IsNil() { + return result[0].Interface(), result[1].Interface().(error) + } + return result[0].Interface(), nil +} + +// Boolean logic. + +func truth(a interface{}) bool { + t, _ := isTrue(reflect.ValueOf(a)) + return t +} + +// and computes the Boolean AND of its arguments, returning +// the first false argument it encounters, or the last argument. +func and(arg0 interface{}, args ...interface{}) interface{} { + if !truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if !truth(arg0) { + break + } + } + return arg0 +} + +// or computes the Boolean OR of its arguments, returning +// the first true argument it encounters, or the last argument. +func or(arg0 interface{}, args ...interface{}) interface{} { + if truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if truth(arg0) { + break + } + } + return arg0 +} + +// not returns the Boolean negation of its argument. +func not(arg interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg)) + return !truth +} + +// Comparison. + +// TODO: Perhaps allow comparison between signed and unsigned integers. + +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +// eq evaluates the comparison a == b || a == c || ... +func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + if len(arg2) == 0 { + return false, errNoComparison + } + for _, arg := range arg2 { + v2 := reflect.ValueOf(arg) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } + } + if truth { + return true, nil + } + } + return false, nil +} + +// ne evaluates the comparison a != b. +func ne(arg1, arg2 interface{}) (bool, error) { + // != is the inverse of ==. + equal, err := eq(arg1, arg2) + return !equal, err +} + +// lt evaluates the comparison a < b. +func lt(arg1, arg2 interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + v2 := reflect.ValueOf(arg2) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } + } + return truth, nil +} + +// le evaluates the comparison <= b. +func le(arg1, arg2 interface{}) (bool, error) { + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) +} + +// gt evaluates the comparison a > b. +func gt(arg1, arg2 interface{}) (bool, error) { + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil +} + +// ge evaluates the comparison a >= b. +func ge(arg1, arg2 interface{}) (bool, error) { + // >= is the inverse of <. + lessThan, err := lt(arg1, arg2) + if err != nil { + return false, err + } + return !lessThan, nil +} + +// HTML escaping. + +var ( + htmlQuot = []byte(""") // shorter than """ + htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 + htmlAmp = []byte("&") + htmlLt = []byte("<") + htmlGt = []byte(">") +) + +// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. +func HTMLEscape(w io.Writer, b []byte) { + last := 0 + for i, c := range b { + var html []byte + switch c { + case '"': + html = htmlQuot + case '\'': + html = htmlApos + case '&': + html = htmlAmp + case '<': + html = htmlLt + case '>': + html = htmlGt + default: + continue + } + w.Write(b[last:i]) + w.Write(html) + last = i + 1 + } + w.Write(b[last:]) +} + +// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. +func HTMLEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexAny(s, `'"&<>`) < 0 { + return s + } + var b bytes.Buffer + HTMLEscape(&b, []byte(s)) + return b.String() +} + +// HTMLEscaper returns the escaped HTML equivalent of the textual +// representation of its arguments. +func HTMLEscaper(args ...interface{}) string { + return HTMLEscapeString(evalArgs(args)) +} + +// JavaScript escaping. + +var ( + jsLowUni = []byte(`\u00`) + hex = []byte("0123456789ABCDEF") + + jsBackslash = []byte(`\\`) + jsApos = []byte(`\'`) + jsQuot = []byte(`\"`) + jsLt = []byte(`\x3C`) + jsGt = []byte(`\x3E`) +) + +// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. +func JSEscape(w io.Writer, b []byte) { + last := 0 + for i := 0; i < len(b); i++ { + c := b[i] + + if !jsIsSpecial(rune(c)) { + // fast path: nothing to do + continue + } + w.Write(b[last:i]) + + if c < utf8.RuneSelf { + // Quotes, slashes and angle brackets get quoted. + // Control characters get written as \u00XX. + switch c { + case '\\': + w.Write(jsBackslash) + case '\'': + w.Write(jsApos) + case '"': + w.Write(jsQuot) + case '<': + w.Write(jsLt) + case '>': + w.Write(jsGt) + default: + w.Write(jsLowUni) + t, b := c>>4, c&0x0f + w.Write(hex[t : t+1]) + w.Write(hex[b : b+1]) + } + } else { + // Unicode rune. + r, size := utf8.DecodeRune(b[i:]) + if unicode.IsPrint(r) { + w.Write(b[i : i+size]) + } else { + fmt.Fprintf(w, "\\u%04X", r) + } + i += size - 1 + } + last = i + 1 + } + w.Write(b[last:]) +} + +// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. +func JSEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexFunc(s, jsIsSpecial) < 0 { + return s + } + var b bytes.Buffer + JSEscape(&b, []byte(s)) + return b.String() +} + +func jsIsSpecial(r rune) bool { + switch r { + case '\\', '\'', '"', '<', '>': + return true + } + return r < ' ' || utf8.RuneSelf <= r +} + +// JSEscaper returns the escaped JavaScript equivalent of the textual +// representation of its arguments. +func JSEscaper(args ...interface{}) string { + return JSEscapeString(evalArgs(args)) +} + +// URLQueryEscaper returns the escaped value of the textual representation of +// its arguments in a form suitable for embedding in a URL query. +func URLQueryEscaper(args ...interface{}) string { + return url.QueryEscape(evalArgs(args)) +} + +// evalArgs formats the list of arguments into a string. It is therefore equivalent to +// fmt.Sprint(args...) +// except that each argument is indirected (if a pointer), as required, +// using the same rules as the default string evaluation during template +// execution. +func evalArgs(args []interface{}) string { + ok := false + var s string + // Fast path for simple common case. + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + for i, arg := range args { + a, ok := printableValue(reflect.ValueOf(arg)) + if ok { + args[i] = a + } // else left fmt do its thing + } + s = fmt.Sprint(args...) + } + return s +} diff --git a/cli/vendor/github.com/alecthomas/template/helper.go b/cli/vendor/github.com/alecthomas/template/helper.go new file mode 100644 index 0000000..3636fb5 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/helper.go @@ -0,0 +1,108 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Helper functions to make constructing templates easier. + +package template + +import ( + "fmt" + "io/ioutil" + "path/filepath" +) + +// Functions and methods to parse templates. + +// Must is a helper that wraps a call to a function returning (*Template, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var t = template.Must(template.New("name").Parse("text")) +func Must(t *Template, err error) *Template { + if err != nil { + panic(err) + } + return t +} + +// ParseFiles creates a new Template and parses the template definitions from +// the named files. The returned template's name will have the (base) name and +// (parsed) contents of the first file. There must be at least one file. +// If an error occurs, parsing stops and the returned *Template is nil. +func ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(nil, filenames...) +} + +// ParseFiles parses the named files and associates the resulting templates with +// t. If an error occurs, parsing stops and the returned template is nil; +// otherwise it is t. There must be at least one file. +func (t *Template) ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(t, filenames...) +} + +// parseFiles is the helper for the method and function. If the argument +// template is nil, it is created from the first file. +func parseFiles(t *Template, filenames ...string) (*Template, error) { + if len(filenames) == 0 { + // Not really a problem, but be consistent. + return nil, fmt.Errorf("template: no files named in call to ParseFiles") + } + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + s := string(b) + name := filepath.Base(filename) + // First template becomes return value if not already defined, + // and we use that one for subsequent New calls to associate + // all the templates together. Also, if this file has the same name + // as t, this file becomes the contents of t, so + // t, err := New(name).Funcs(xxx).ParseFiles(name) + // works. Otherwise we create a new template associated with t. + var tmpl *Template + if t == nil { + t = New(name) + } + if name == t.Name() { + tmpl = t + } else { + tmpl = t.New(name) + } + _, err = tmpl.Parse(s) + if err != nil { + return nil, err + } + } + return t, nil +} + +// ParseGlob creates a new Template and parses the template definitions from the +// files identified by the pattern, which must match at least one file. The +// returned template will have the (base) name and (parsed) contents of the +// first file matched by the pattern. ParseGlob is equivalent to calling +// ParseFiles with the list of files matched by the pattern. +func ParseGlob(pattern string) (*Template, error) { + return parseGlob(nil, pattern) +} + +// ParseGlob parses the template definitions in the files identified by the +// pattern and associates the resulting templates with t. The pattern is +// processed by filepath.Glob and must match at least one file. ParseGlob is +// equivalent to calling t.ParseFiles with the list of files matched by the +// pattern. +func (t *Template) ParseGlob(pattern string) (*Template, error) { + return parseGlob(t, pattern) +} + +// parseGlob is the implementation of the function and method ParseGlob. +func parseGlob(t *Template, pattern string) (*Template, error) { + filenames, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + if len(filenames) == 0 { + return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) + } + return parseFiles(t, filenames...) +} diff --git a/cli/vendor/github.com/alecthomas/template/parse/lex.go b/cli/vendor/github.com/alecthomas/template/parse/lex.go new file mode 100644 index 0000000..55f1c05 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/parse/lex.go @@ -0,0 +1,556 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package parse + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +// item represents a token or text string returned from the scanner. +type item struct { + typ itemType // The type of this item. + pos Pos // The starting position, in bytes, of this item in the input string. + val string // The value of this item. +} + +func (i item) String() string { + switch { + case i.typ == itemEOF: + return "EOF" + case i.typ == itemError: + return i.val + case i.typ > itemKeyword: + return fmt.Sprintf("<%s>", i.val) + case len(i.val) > 10: + return fmt.Sprintf("%.10q...", i.val) + } + return fmt.Sprintf("%q", i.val) +} + +// itemType identifies the type of lex items. +type itemType int + +const ( + itemError itemType = iota // error occurred; value is text of error + itemBool // boolean constant + itemChar // printable ASCII character; grab bag for comma etc. + itemCharConstant // character constant + itemComplex // complex constant (1+2i); imaginary is just a number + itemColonEquals // colon-equals (':=') introducing a declaration + itemEOF + itemField // alphanumeric identifier starting with '.' + itemIdentifier // alphanumeric identifier not starting with '.' + itemLeftDelim // left action delimiter + itemLeftParen // '(' inside action + itemNumber // simple number, including imaginary + itemPipe // pipe symbol + itemRawString // raw quoted string (includes quotes) + itemRightDelim // right action delimiter + itemElideNewline // elide newline after right delim + itemRightParen // ')' inside action + itemSpace // run of spaces separating arguments + itemString // quoted string (includes quotes) + itemText // plain text + itemVariable // variable starting with '$', such as '$' or '$1' or '$hello' + // Keywords appear after all the rest. + itemKeyword // used only to delimit the keywords + itemDot // the cursor, spelled '.' + itemDefine // define keyword + itemElse // else keyword + itemEnd // end keyword + itemIf // if keyword + itemNil // the untyped nil constant, easiest to treat as a keyword + itemRange // range keyword + itemTemplate // template keyword + itemWith // with keyword +) + +var key = map[string]itemType{ + ".": itemDot, + "define": itemDefine, + "else": itemElse, + "end": itemEnd, + "if": itemIf, + "range": itemRange, + "nil": itemNil, + "template": itemTemplate, + "with": itemWith, +} + +const eof = -1 + +// stateFn represents the state of the scanner as a function that returns the next state. +type stateFn func(*lexer) stateFn + +// lexer holds the state of the scanner. +type lexer struct { + name string // the name of the input; used only for error reports + input string // the string being scanned + leftDelim string // start of action + rightDelim string // end of action + state stateFn // the next lexing function to enter + pos Pos // current position in the input + start Pos // start position of this item + width Pos // width of last rune read from input + lastPos Pos // position of most recent item returned by nextItem + items chan item // channel of scanned items + parenDepth int // nesting depth of ( ) exprs +} + +// next returns the next rune in the input. +func (l *lexer) next() rune { + if int(l.pos) >= len(l.input) { + l.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(l.input[l.pos:]) + l.width = Pos(w) + l.pos += l.width + return r +} + +// peek returns but does not consume the next rune in the input. +func (l *lexer) peek() rune { + r := l.next() + l.backup() + return r +} + +// backup steps back one rune. Can only be called once per call of next. +func (l *lexer) backup() { + l.pos -= l.width +} + +// emit passes an item back to the client. +func (l *lexer) emit(t itemType) { + l.items <- item{t, l.start, l.input[l.start:l.pos]} + l.start = l.pos +} + +// ignore skips over the pending input before this point. +func (l *lexer) ignore() { + l.start = l.pos +} + +// accept consumes the next rune if it's from the valid set. +func (l *lexer) accept(valid string) bool { + if strings.IndexRune(valid, l.next()) >= 0 { + return true + } + l.backup() + return false +} + +// acceptRun consumes a run of runes from the valid set. +func (l *lexer) acceptRun(valid string) { + for strings.IndexRune(valid, l.next()) >= 0 { + } + l.backup() +} + +// lineNumber reports which line we're on, based on the position of +// the previous item returned by nextItem. Doing it this way +// means we don't have to worry about peek double counting. +func (l *lexer) lineNumber() int { + return 1 + strings.Count(l.input[:l.lastPos], "\n") +} + +// errorf returns an error token and terminates the scan by passing +// back a nil pointer that will be the next state, terminating l.nextItem. +func (l *lexer) errorf(format string, args ...interface{}) stateFn { + l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} + return nil +} + +// nextItem returns the next item from the input. +func (l *lexer) nextItem() item { + item := <-l.items + l.lastPos = item.pos + return item +} + +// lex creates a new scanner for the input string. +func lex(name, input, left, right string) *lexer { + if left == "" { + left = leftDelim + } + if right == "" { + right = rightDelim + } + l := &lexer{ + name: name, + input: input, + leftDelim: left, + rightDelim: right, + items: make(chan item), + } + go l.run() + return l +} + +// run runs the state machine for the lexer. +func (l *lexer) run() { + for l.state = lexText; l.state != nil; { + l.state = l.state(l) + } +} + +// state functions + +const ( + leftDelim = "{{" + rightDelim = "}}" + leftComment = "/*" + rightComment = "*/" +) + +// lexText scans until an opening action delimiter, "{{". +func lexText(l *lexer) stateFn { + for { + if strings.HasPrefix(l.input[l.pos:], l.leftDelim) { + if l.pos > l.start { + l.emit(itemText) + } + return lexLeftDelim + } + if l.next() == eof { + break + } + } + // Correctly reached EOF. + if l.pos > l.start { + l.emit(itemText) + } + l.emit(itemEOF) + return nil +} + +// lexLeftDelim scans the left delimiter, which is known to be present. +func lexLeftDelim(l *lexer) stateFn { + l.pos += Pos(len(l.leftDelim)) + if strings.HasPrefix(l.input[l.pos:], leftComment) { + return lexComment + } + l.emit(itemLeftDelim) + l.parenDepth = 0 + return lexInsideAction +} + +// lexComment scans a comment. The left comment marker is known to be present. +func lexComment(l *lexer) stateFn { + l.pos += Pos(len(leftComment)) + i := strings.Index(l.input[l.pos:], rightComment) + if i < 0 { + return l.errorf("unclosed comment") + } + l.pos += Pos(i + len(rightComment)) + if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) { + return l.errorf("comment ends before closing delimiter") + + } + l.pos += Pos(len(l.rightDelim)) + l.ignore() + return lexText +} + +// lexRightDelim scans the right delimiter, which is known to be present. +func lexRightDelim(l *lexer) stateFn { + l.pos += Pos(len(l.rightDelim)) + l.emit(itemRightDelim) + if l.peek() == '\\' { + l.pos++ + l.emit(itemElideNewline) + } + return lexText +} + +// lexInsideAction scans the elements inside action delimiters. +func lexInsideAction(l *lexer) stateFn { + // Either number, quoted string, or identifier. + // Spaces separate arguments; runs of spaces turn into itemSpace. + // Pipe symbols separate and are emitted. + if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) { + if l.parenDepth == 0 { + return lexRightDelim + } + return l.errorf("unclosed left paren") + } + switch r := l.next(); { + case r == eof || isEndOfLine(r): + return l.errorf("unclosed action") + case isSpace(r): + return lexSpace + case r == ':': + if l.next() != '=' { + return l.errorf("expected :=") + } + l.emit(itemColonEquals) + case r == '|': + l.emit(itemPipe) + case r == '"': + return lexQuote + case r == '`': + return lexRawQuote + case r == '$': + return lexVariable + case r == '\'': + return lexChar + case r == '.': + // special look-ahead for ".field" so we don't break l.backup(). + if l.pos < Pos(len(l.input)) { + r := l.input[l.pos] + if r < '0' || '9' < r { + return lexField + } + } + fallthrough // '.' can start a number. + case r == '+' || r == '-' || ('0' <= r && r <= '9'): + l.backup() + return lexNumber + case isAlphaNumeric(r): + l.backup() + return lexIdentifier + case r == '(': + l.emit(itemLeftParen) + l.parenDepth++ + return lexInsideAction + case r == ')': + l.emit(itemRightParen) + l.parenDepth-- + if l.parenDepth < 0 { + return l.errorf("unexpected right paren %#U", r) + } + return lexInsideAction + case r <= unicode.MaxASCII && unicode.IsPrint(r): + l.emit(itemChar) + return lexInsideAction + default: + return l.errorf("unrecognized character in action: %#U", r) + } + return lexInsideAction +} + +// lexSpace scans a run of space characters. +// One space has already been seen. +func lexSpace(l *lexer) stateFn { + for isSpace(l.peek()) { + l.next() + } + l.emit(itemSpace) + return lexInsideAction +} + +// lexIdentifier scans an alphanumeric. +func lexIdentifier(l *lexer) stateFn { +Loop: + for { + switch r := l.next(); { + case isAlphaNumeric(r): + // absorb. + default: + l.backup() + word := l.input[l.start:l.pos] + if !l.atTerminator() { + return l.errorf("bad character %#U", r) + } + switch { + case key[word] > itemKeyword: + l.emit(key[word]) + case word[0] == '.': + l.emit(itemField) + case word == "true", word == "false": + l.emit(itemBool) + default: + l.emit(itemIdentifier) + } + break Loop + } + } + return lexInsideAction +} + +// lexField scans a field: .Alphanumeric. +// The . has been scanned. +func lexField(l *lexer) stateFn { + return lexFieldOrVariable(l, itemField) +} + +// lexVariable scans a Variable: $Alphanumeric. +// The $ has been scanned. +func lexVariable(l *lexer) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "$". + l.emit(itemVariable) + return lexInsideAction + } + return lexFieldOrVariable(l, itemVariable) +} + +// lexVariable scans a field or variable: [.$]Alphanumeric. +// The . or $ has been scanned. +func lexFieldOrVariable(l *lexer, typ itemType) stateFn { + if l.atTerminator() { // Nothing interesting follows -> "." or "$". + if typ == itemVariable { + l.emit(itemVariable) + } else { + l.emit(itemDot) + } + return lexInsideAction + } + var r rune + for { + r = l.next() + if !isAlphaNumeric(r) { + l.backup() + break + } + } + if !l.atTerminator() { + return l.errorf("bad character %#U", r) + } + l.emit(typ) + return lexInsideAction +} + +// atTerminator reports whether the input is at valid termination character to +// appear after an identifier. Breaks .X.Y into two pieces. Also catches cases +// like "$x+2" not being acceptable without a space, in case we decide one +// day to implement arithmetic. +func (l *lexer) atTerminator() bool { + r := l.peek() + if isSpace(r) || isEndOfLine(r) { + return true + } + switch r { + case eof, '.', ',', '|', ':', ')', '(': + return true + } + // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will + // succeed but should fail) but only in extremely rare cases caused by willfully + // bad choice of delimiter. + if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r { + return true + } + return false +} + +// lexChar scans a character constant. The initial quote is already +// scanned. Syntax checking is done by the parser. +func lexChar(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated character constant") + case '\'': + break Loop + } + } + l.emit(itemCharConstant) + return lexInsideAction +} + +// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This +// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" +// and "089" - but when it's wrong the input is invalid and the parser (via +// strconv) will notice. +func lexNumber(l *lexer) stateFn { + if !l.scanNumber() { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + if sign := l.peek(); sign == '+' || sign == '-' { + // Complex: 1+2i. No spaces, must end in 'i'. + if !l.scanNumber() || l.input[l.pos-1] != 'i' { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + l.emit(itemComplex) + } else { + l.emit(itemNumber) + } + return lexInsideAction +} + +func (l *lexer) scanNumber() bool { + // Optional leading sign. + l.accept("+-") + // Is it hex? + digits := "0123456789" + if l.accept("0") && l.accept("xX") { + digits = "0123456789abcdefABCDEF" + } + l.acceptRun(digits) + if l.accept(".") { + l.acceptRun(digits) + } + if l.accept("eE") { + l.accept("+-") + l.acceptRun("0123456789") + } + // Is it imaginary? + l.accept("i") + // Next thing mustn't be alphanumeric. + if isAlphaNumeric(l.peek()) { + l.next() + return false + } + return true +} + +// lexQuote scans a quoted string. +func lexQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated quoted string") + case '"': + break Loop + } + } + l.emit(itemString) + return lexInsideAction +} + +// lexRawQuote scans a raw quoted string. +func lexRawQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case eof, '\n': + return l.errorf("unterminated raw quoted string") + case '`': + break Loop + } + } + l.emit(itemRawString) + return lexInsideAction +} + +// isSpace reports whether r is a space character. +func isSpace(r rune) bool { + return r == ' ' || r == '\t' +} + +// isEndOfLine reports whether r is an end-of-line character. +func isEndOfLine(r rune) bool { + return r == '\r' || r == '\n' +} + +// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. +func isAlphaNumeric(r rune) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} diff --git a/cli/vendor/github.com/alecthomas/template/parse/node.go b/cli/vendor/github.com/alecthomas/template/parse/node.go new file mode 100644 index 0000000..55c37f6 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/parse/node.go @@ -0,0 +1,834 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parse nodes. + +package parse + +import ( + "bytes" + "fmt" + "strconv" + "strings" +) + +var textFormat = "%s" // Changed to "%q" in tests for better error messages. + +// A Node is an element in the parse tree. The interface is trivial. +// The interface contains an unexported method so that only +// types local to this package can satisfy it. +type Node interface { + Type() NodeType + String() string + // Copy does a deep copy of the Node and all its components. + // To avoid type assertions, some XxxNodes also have specialized + // CopyXxx methods that return *XxxNode. + Copy() Node + Position() Pos // byte position of start of node in full original input string + // tree returns the containing *Tree. + // It is unexported so all implementations of Node are in this package. + tree() *Tree +} + +// NodeType identifies the type of a parse tree node. +type NodeType int + +// Pos represents a byte position in the original input text from which +// this template was parsed. +type Pos int + +func (p Pos) Position() Pos { + return p +} + +// Type returns itself and provides an easy default implementation +// for embedding in a Node. Embedded in all non-trivial Nodes. +func (t NodeType) Type() NodeType { + return t +} + +const ( + NodeText NodeType = iota // Plain text. + NodeAction // A non-control action such as a field evaluation. + NodeBool // A boolean constant. + NodeChain // A sequence of field accesses. + NodeCommand // An element of a pipeline. + NodeDot // The cursor, dot. + nodeElse // An else action. Not added to tree. + nodeEnd // An end action. Not added to tree. + NodeField // A field or method name. + NodeIdentifier // An identifier; always a function name. + NodeIf // An if action. + NodeList // A list of Nodes. + NodeNil // An untyped nil constant. + NodeNumber // A numerical constant. + NodePipe // A pipeline of commands. + NodeRange // A range action. + NodeString // A string constant. + NodeTemplate // A template invocation action. + NodeVariable // A $ variable. + NodeWith // A with action. +) + +// Nodes. + +// ListNode holds a sequence of nodes. +type ListNode struct { + NodeType + Pos + tr *Tree + Nodes []Node // The element nodes in lexical order. +} + +func (t *Tree) newList(pos Pos) *ListNode { + return &ListNode{tr: t, NodeType: NodeList, Pos: pos} +} + +func (l *ListNode) append(n Node) { + l.Nodes = append(l.Nodes, n) +} + +func (l *ListNode) tree() *Tree { + return l.tr +} + +func (l *ListNode) String() string { + b := new(bytes.Buffer) + for _, n := range l.Nodes { + fmt.Fprint(b, n) + } + return b.String() +} + +func (l *ListNode) CopyList() *ListNode { + if l == nil { + return l + } + n := l.tr.newList(l.Pos) + for _, elem := range l.Nodes { + n.append(elem.Copy()) + } + return n +} + +func (l *ListNode) Copy() Node { + return l.CopyList() +} + +// TextNode holds plain text. +type TextNode struct { + NodeType + Pos + tr *Tree + Text []byte // The text; may span newlines. +} + +func (t *Tree) newText(pos Pos, text string) *TextNode { + return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} +} + +func (t *TextNode) String() string { + return fmt.Sprintf(textFormat, t.Text) +} + +func (t *TextNode) tree() *Tree { + return t.tr +} + +func (t *TextNode) Copy() Node { + return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} +} + +// PipeNode holds a pipeline with optional declaration +type PipeNode struct { + NodeType + Pos + tr *Tree + Line int // The line number in the input (deprecated; kept for compatibility) + Decl []*VariableNode // Variable declarations in lexical order. + Cmds []*CommandNode // The commands in lexical order. +} + +func (t *Tree) newPipeline(pos Pos, line int, decl []*VariableNode) *PipeNode { + return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: decl} +} + +func (p *PipeNode) append(command *CommandNode) { + p.Cmds = append(p.Cmds, command) +} + +func (p *PipeNode) String() string { + s := "" + if len(p.Decl) > 0 { + for i, v := range p.Decl { + if i > 0 { + s += ", " + } + s += v.String() + } + s += " := " + } + for i, c := range p.Cmds { + if i > 0 { + s += " | " + } + s += c.String() + } + return s +} + +func (p *PipeNode) tree() *Tree { + return p.tr +} + +func (p *PipeNode) CopyPipe() *PipeNode { + if p == nil { + return p + } + var decl []*VariableNode + for _, d := range p.Decl { + decl = append(decl, d.Copy().(*VariableNode)) + } + n := p.tr.newPipeline(p.Pos, p.Line, decl) + for _, c := range p.Cmds { + n.append(c.Copy().(*CommandNode)) + } + return n +} + +func (p *PipeNode) Copy() Node { + return p.CopyPipe() +} + +// ActionNode holds an action (something bounded by delimiters). +// Control actions have their own nodes; ActionNode represents simple +// ones such as field evaluations and parenthesized pipelines. +type ActionNode struct { + NodeType + Pos + tr *Tree + Line int // The line number in the input (deprecated; kept for compatibility) + Pipe *PipeNode // The pipeline in the action. +} + +func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { + return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} +} + +func (a *ActionNode) String() string { + return fmt.Sprintf("{{%s}}", a.Pipe) + +} + +func (a *ActionNode) tree() *Tree { + return a.tr +} + +func (a *ActionNode) Copy() Node { + return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) + +} + +// CommandNode holds a command (a pipeline inside an evaluating action). +type CommandNode struct { + NodeType + Pos + tr *Tree + Args []Node // Arguments in lexical order: Identifier, field, or constant. +} + +func (t *Tree) newCommand(pos Pos) *CommandNode { + return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} +} + +func (c *CommandNode) append(arg Node) { + c.Args = append(c.Args, arg) +} + +func (c *CommandNode) String() string { + s := "" + for i, arg := range c.Args { + if i > 0 { + s += " " + } + if arg, ok := arg.(*PipeNode); ok { + s += "(" + arg.String() + ")" + continue + } + s += arg.String() + } + return s +} + +func (c *CommandNode) tree() *Tree { + return c.tr +} + +func (c *CommandNode) Copy() Node { + if c == nil { + return c + } + n := c.tr.newCommand(c.Pos) + for _, c := range c.Args { + n.append(c.Copy()) + } + return n +} + +// IdentifierNode holds an identifier. +type IdentifierNode struct { + NodeType + Pos + tr *Tree + Ident string // The identifier's name. +} + +// NewIdentifier returns a new IdentifierNode with the given identifier name. +func NewIdentifier(ident string) *IdentifierNode { + return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} +} + +// SetPos sets the position. NewIdentifier is a public method so we can't modify its signature. +// Chained for convenience. +// TODO: fix one day? +func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { + i.Pos = pos + return i +} + +// SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature. +// Chained for convenience. +// TODO: fix one day? +func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { + i.tr = t + return i +} + +func (i *IdentifierNode) String() string { + return i.Ident +} + +func (i *IdentifierNode) tree() *Tree { + return i.tr +} + +func (i *IdentifierNode) Copy() Node { + return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) +} + +// VariableNode holds a list of variable names, possibly with chained field +// accesses. The dollar sign is part of the (first) name. +type VariableNode struct { + NodeType + Pos + tr *Tree + Ident []string // Variable name and fields in lexical order. +} + +func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { + return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} +} + +func (v *VariableNode) String() string { + s := "" + for i, id := range v.Ident { + if i > 0 { + s += "." + } + s += id + } + return s +} + +func (v *VariableNode) tree() *Tree { + return v.tr +} + +func (v *VariableNode) Copy() Node { + return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} +} + +// DotNode holds the special identifier '.'. +type DotNode struct { + NodeType + Pos + tr *Tree +} + +func (t *Tree) newDot(pos Pos) *DotNode { + return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} +} + +func (d *DotNode) Type() NodeType { + // Override method on embedded NodeType for API compatibility. + // TODO: Not really a problem; could change API without effect but + // api tool complains. + return NodeDot +} + +func (d *DotNode) String() string { + return "." +} + +func (d *DotNode) tree() *Tree { + return d.tr +} + +func (d *DotNode) Copy() Node { + return d.tr.newDot(d.Pos) +} + +// NilNode holds the special identifier 'nil' representing an untyped nil constant. +type NilNode struct { + NodeType + Pos + tr *Tree +} + +func (t *Tree) newNil(pos Pos) *NilNode { + return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} +} + +func (n *NilNode) Type() NodeType { + // Override method on embedded NodeType for API compatibility. + // TODO: Not really a problem; could change API without effect but + // api tool complains. + return NodeNil +} + +func (n *NilNode) String() string { + return "nil" +} + +func (n *NilNode) tree() *Tree { + return n.tr +} + +func (n *NilNode) Copy() Node { + return n.tr.newNil(n.Pos) +} + +// FieldNode holds a field (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The period is dropped from each ident. +type FieldNode struct { + NodeType + Pos + tr *Tree + Ident []string // The identifiers in lexical order. +} + +func (t *Tree) newField(pos Pos, ident string) *FieldNode { + return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +} + +func (f *FieldNode) String() string { + s := "" + for _, id := range f.Ident { + s += "." + id + } + return s +} + +func (f *FieldNode) tree() *Tree { + return f.tr +} + +func (f *FieldNode) Copy() Node { + return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} +} + +// ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The periods are dropped from each ident. +type ChainNode struct { + NodeType + Pos + tr *Tree + Node Node + Field []string // The identifiers in lexical order. +} + +func (t *Tree) newChain(pos Pos, node Node) *ChainNode { + return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node} +} + +// Add adds the named field (which should start with a period) to the end of the chain. +func (c *ChainNode) Add(field string) { + if len(field) == 0 || field[0] != '.' { + panic("no dot in field") + } + field = field[1:] // Remove leading dot. + if field == "" { + panic("empty field") + } + c.Field = append(c.Field, field) +} + +func (c *ChainNode) String() string { + s := c.Node.String() + if _, ok := c.Node.(*PipeNode); ok { + s = "(" + s + ")" + } + for _, field := range c.Field { + s += "." + field + } + return s +} + +func (c *ChainNode) tree() *Tree { + return c.tr +} + +func (c *ChainNode) Copy() Node { + return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} +} + +// BoolNode holds a boolean constant. +type BoolNode struct { + NodeType + Pos + tr *Tree + True bool // The value of the boolean constant. +} + +func (t *Tree) newBool(pos Pos, true bool) *BoolNode { + return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} +} + +func (b *BoolNode) String() string { + if b.True { + return "true" + } + return "false" +} + +func (b *BoolNode) tree() *Tree { + return b.tr +} + +func (b *BoolNode) Copy() Node { + return b.tr.newBool(b.Pos, b.True) +} + +// NumberNode holds a number: signed or unsigned integer, float, or complex. +// The value is parsed and stored under all the types that can represent the value. +// This simulates in a small amount of code the behavior of Go's ideal constants. +type NumberNode struct { + NodeType + Pos + tr *Tree + IsInt bool // Number has an integral value. + IsUint bool // Number has an unsigned integral value. + IsFloat bool // Number has a floating-point value. + IsComplex bool // Number is complex. + Int64 int64 // The signed integer value. + Uint64 uint64 // The unsigned integer value. + Float64 float64 // The floating-point value. + Complex128 complex128 // The complex value. + Text string // The original textual representation from the input. +} + +func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { + n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text} + switch typ { + case itemCharConstant: + rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) + if err != nil { + return nil, err + } + if tail != "'" { + return nil, fmt.Errorf("malformed character constant: %s", text) + } + n.Int64 = int64(rune) + n.IsInt = true + n.Uint64 = uint64(rune) + n.IsUint = true + n.Float64 = float64(rune) // odd but those are the rules. + n.IsFloat = true + return n, nil + case itemComplex: + // fmt.Sscan can parse the pair, so let it do the work. + if _, err := fmt.Sscan(text, &n.Complex128); err != nil { + return nil, err + } + n.IsComplex = true + n.simplifyComplex() + return n, nil + } + // Imaginary constants can only be complex unless they are zero. + if len(text) > 0 && text[len(text)-1] == 'i' { + f, err := strconv.ParseFloat(text[:len(text)-1], 64) + if err == nil { + n.IsComplex = true + n.Complex128 = complex(0, f) + n.simplifyComplex() + return n, nil + } + } + // Do integer test first so we get 0x123 etc. + u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below. + if err == nil { + n.IsUint = true + n.Uint64 = u + } + i, err := strconv.ParseInt(text, 0, 64) + if err == nil { + n.IsInt = true + n.Int64 = i + if i == 0 { + n.IsUint = true // in case of -0. + n.Uint64 = u + } + } + // If an integer extraction succeeded, promote the float. + if n.IsInt { + n.IsFloat = true + n.Float64 = float64(n.Int64) + } else if n.IsUint { + n.IsFloat = true + n.Float64 = float64(n.Uint64) + } else { + f, err := strconv.ParseFloat(text, 64) + if err == nil { + n.IsFloat = true + n.Float64 = f + // If a floating-point extraction succeeded, extract the int if needed. + if !n.IsInt && float64(int64(f)) == f { + n.IsInt = true + n.Int64 = int64(f) + } + if !n.IsUint && float64(uint64(f)) == f { + n.IsUint = true + n.Uint64 = uint64(f) + } + } + } + if !n.IsInt && !n.IsUint && !n.IsFloat { + return nil, fmt.Errorf("illegal number syntax: %q", text) + } + return n, nil +} + +// simplifyComplex pulls out any other types that are represented by the complex number. +// These all require that the imaginary part be zero. +func (n *NumberNode) simplifyComplex() { + n.IsFloat = imag(n.Complex128) == 0 + if n.IsFloat { + n.Float64 = real(n.Complex128) + n.IsInt = float64(int64(n.Float64)) == n.Float64 + if n.IsInt { + n.Int64 = int64(n.Float64) + } + n.IsUint = float64(uint64(n.Float64)) == n.Float64 + if n.IsUint { + n.Uint64 = uint64(n.Float64) + } + } +} + +func (n *NumberNode) String() string { + return n.Text +} + +func (n *NumberNode) tree() *Tree { + return n.tr +} + +func (n *NumberNode) Copy() Node { + nn := new(NumberNode) + *nn = *n // Easy, fast, correct. + return nn +} + +// StringNode holds a string constant. The value has been "unquoted". +type StringNode struct { + NodeType + Pos + tr *Tree + Quoted string // The original text of the string, with quotes. + Text string // The string, after quote processing. +} + +func (t *Tree) newString(pos Pos, orig, text string) *StringNode { + return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} +} + +func (s *StringNode) String() string { + return s.Quoted +} + +func (s *StringNode) tree() *Tree { + return s.tr +} + +func (s *StringNode) Copy() Node { + return s.tr.newString(s.Pos, s.Quoted, s.Text) +} + +// endNode represents an {{end}} action. +// It does not appear in the final parse tree. +type endNode struct { + NodeType + Pos + tr *Tree +} + +func (t *Tree) newEnd(pos Pos) *endNode { + return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} +} + +func (e *endNode) String() string { + return "{{end}}" +} + +func (e *endNode) tree() *Tree { + return e.tr +} + +func (e *endNode) Copy() Node { + return e.tr.newEnd(e.Pos) +} + +// elseNode represents an {{else}} action. Does not appear in the final tree. +type elseNode struct { + NodeType + Pos + tr *Tree + Line int // The line number in the input (deprecated; kept for compatibility) +} + +func (t *Tree) newElse(pos Pos, line int) *elseNode { + return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} +} + +func (e *elseNode) Type() NodeType { + return nodeElse +} + +func (e *elseNode) String() string { + return "{{else}}" +} + +func (e *elseNode) tree() *Tree { + return e.tr +} + +func (e *elseNode) Copy() Node { + return e.tr.newElse(e.Pos, e.Line) +} + +// BranchNode is the common representation of if, range, and with. +type BranchNode struct { + NodeType + Pos + tr *Tree + Line int // The line number in the input (deprecated; kept for compatibility) + Pipe *PipeNode // The pipeline to be evaluated. + List *ListNode // What to execute if the value is non-empty. + ElseList *ListNode // What to execute if the value is empty (nil if absent). +} + +func (b *BranchNode) String() string { + name := "" + switch b.NodeType { + case NodeIf: + name = "if" + case NodeRange: + name = "range" + case NodeWith: + name = "with" + default: + panic("unknown branch type") + } + if b.ElseList != nil { + return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList) + } + return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List) +} + +func (b *BranchNode) tree() *Tree { + return b.tr +} + +func (b *BranchNode) Copy() Node { + switch b.NodeType { + case NodeIf: + return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + case NodeRange: + return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + case NodeWith: + return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) + default: + panic("unknown branch type") + } +} + +// IfNode represents an {{if}} action and its commands. +type IfNode struct { + BranchNode +} + +func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { + return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +} + +func (i *IfNode) Copy() Node { + return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) +} + +// RangeNode represents a {{range}} action and its commands. +type RangeNode struct { + BranchNode +} + +func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { + return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +} + +func (r *RangeNode) Copy() Node { + return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) +} + +// WithNode represents a {{with}} action and its commands. +type WithNode struct { + BranchNode +} + +func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { + return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} +} + +func (w *WithNode) Copy() Node { + return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) +} + +// TemplateNode represents a {{template}} action. +type TemplateNode struct { + NodeType + Pos + tr *Tree + Line int // The line number in the input (deprecated; kept for compatibility) + Name string // The name of the template (unquoted). + Pipe *PipeNode // The command to evaluate as dot for the template. +} + +func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { + return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe} +} + +func (t *TemplateNode) String() string { + if t.Pipe == nil { + return fmt.Sprintf("{{template %q}}", t.Name) + } + return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) +} + +func (t *TemplateNode) tree() *Tree { + return t.tr +} + +func (t *TemplateNode) Copy() Node { + return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) +} diff --git a/cli/vendor/github.com/alecthomas/template/parse/parse.go b/cli/vendor/github.com/alecthomas/template/parse/parse.go new file mode 100644 index 0000000..0d77ade --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/parse/parse.go @@ -0,0 +1,700 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package parse builds parse trees for templates as defined by text/template +// and html/template. Clients should use those packages to construct templates +// rather than this one, which provides shared internal data structures not +// intended for general use. +package parse + +import ( + "bytes" + "fmt" + "runtime" + "strconv" + "strings" +) + +// Tree is the representation of a single parsed template. +type Tree struct { + Name string // name of the template represented by the tree. + ParseName string // name of the top-level template during parsing, for error messages. + Root *ListNode // top-level root of the tree. + text string // text parsed to create the template (or its parent) + // Parsing only; cleared after parse. + funcs []map[string]interface{} + lex *lexer + token [3]item // three-token lookahead for parser. + peekCount int + vars []string // variables defined at the moment. +} + +// Copy returns a copy of the Tree. Any parsing state is discarded. +func (t *Tree) Copy() *Tree { + if t == nil { + return nil + } + return &Tree{ + Name: t.Name, + ParseName: t.ParseName, + Root: t.Root.CopyList(), + text: t.text, + } +} + +// Parse returns a map from template name to parse.Tree, created by parsing the +// templates described in the argument string. The top-level template will be +// given the specified name. If an error is encountered, parsing stops and an +// empty map is returned with the error. +func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) { + treeSet = make(map[string]*Tree) + t := New(name) + t.text = text + _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...) + return +} + +// next returns the next token. +func (t *Tree) next() item { + if t.peekCount > 0 { + t.peekCount-- + } else { + t.token[0] = t.lex.nextItem() + } + return t.token[t.peekCount] +} + +// backup backs the input stream up one token. +func (t *Tree) backup() { + t.peekCount++ +} + +// backup2 backs the input stream up two tokens. +// The zeroth token is already there. +func (t *Tree) backup2(t1 item) { + t.token[1] = t1 + t.peekCount = 2 +} + +// backup3 backs the input stream up three tokens +// The zeroth token is already there. +func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back. + t.token[1] = t1 + t.token[2] = t2 + t.peekCount = 3 +} + +// peek returns but does not consume the next token. +func (t *Tree) peek() item { + if t.peekCount > 0 { + return t.token[t.peekCount-1] + } + t.peekCount = 1 + t.token[0] = t.lex.nextItem() + return t.token[0] +} + +// nextNonSpace returns the next non-space token. +func (t *Tree) nextNonSpace() (token item) { + for { + token = t.next() + if token.typ != itemSpace { + break + } + } + return token +} + +// peekNonSpace returns but does not consume the next non-space token. +func (t *Tree) peekNonSpace() (token item) { + for { + token = t.next() + if token.typ != itemSpace { + break + } + } + t.backup() + return token +} + +// Parsing. + +// New allocates a new parse tree with the given name. +func New(name string, funcs ...map[string]interface{}) *Tree { + return &Tree{ + Name: name, + funcs: funcs, + } +} + +// ErrorContext returns a textual representation of the location of the node in the input text. +// The receiver is only used when the node does not have a pointer to the tree inside, +// which can occur in old code. +func (t *Tree) ErrorContext(n Node) (location, context string) { + pos := int(n.Position()) + tree := n.tree() + if tree == nil { + tree = t + } + text := tree.text[:pos] + byteNum := strings.LastIndex(text, "\n") + if byteNum == -1 { + byteNum = pos // On first line. + } else { + byteNum++ // After the newline. + byteNum = pos - byteNum + } + lineNum := 1 + strings.Count(text, "\n") + context = n.String() + if len(context) > 20 { + context = fmt.Sprintf("%.20s...", context) + } + return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context +} + +// errorf formats the error and terminates processing. +func (t *Tree) errorf(format string, args ...interface{}) { + t.Root = nil + format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format) + panic(fmt.Errorf(format, args...)) +} + +// error terminates processing. +func (t *Tree) error(err error) { + t.errorf("%s", err) +} + +// expect consumes the next token and guarantees it has the required type. +func (t *Tree) expect(expected itemType, context string) item { + token := t.nextNonSpace() + if token.typ != expected { + t.unexpected(token, context) + } + return token +} + +// expectOneOf consumes the next token and guarantees it has one of the required types. +func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { + token := t.nextNonSpace() + if token.typ != expected1 && token.typ != expected2 { + t.unexpected(token, context) + } + return token +} + +// unexpected complains about the token and terminates processing. +func (t *Tree) unexpected(token item, context string) { + t.errorf("unexpected %s in %s", token, context) +} + +// recover is the handler that turns panics into returns from the top level of Parse. +func (t *Tree) recover(errp *error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + if t != nil { + t.stopParse() + } + *errp = e.(error) + } + return +} + +// startParse initializes the parser, using the lexer. +func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) { + t.Root = nil + t.lex = lex + t.vars = []string{"$"} + t.funcs = funcs +} + +// stopParse terminates parsing. +func (t *Tree) stopParse() { + t.lex = nil + t.vars = nil + t.funcs = nil +} + +// Parse parses the template definition string to construct a representation of +// the template for execution. If either action delimiter string is empty, the +// default ("{{" or "}}") is used. Embedded template definitions are added to +// the treeSet map. +func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { + defer t.recover(&err) + t.ParseName = t.Name + t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim)) + t.text = text + t.parse(treeSet) + t.add(treeSet) + t.stopParse() + return t, nil +} + +// add adds tree to the treeSet. +func (t *Tree) add(treeSet map[string]*Tree) { + tree := treeSet[t.Name] + if tree == nil || IsEmptyTree(tree.Root) { + treeSet[t.Name] = t + return + } + if !IsEmptyTree(t.Root) { + t.errorf("template: multiple definition of template %q", t.Name) + } +} + +// IsEmptyTree reports whether this tree (node) is empty of everything but space. +func IsEmptyTree(n Node) bool { + switch n := n.(type) { + case nil: + return true + case *ActionNode: + case *IfNode: + case *ListNode: + for _, node := range n.Nodes { + if !IsEmptyTree(node) { + return false + } + } + return true + case *RangeNode: + case *TemplateNode: + case *TextNode: + return len(bytes.TrimSpace(n.Text)) == 0 + case *WithNode: + default: + panic("unknown node: " + n.String()) + } + return false +} + +// parse is the top-level parser for a template, essentially the same +// as itemList except it also parses {{define}} actions. +// It runs to EOF. +func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { + t.Root = t.newList(t.peek().pos) + for t.peek().typ != itemEOF { + if t.peek().typ == itemLeftDelim { + delim := t.next() + if t.nextNonSpace().typ == itemDefine { + newT := New("definition") // name will be updated once we know it. + newT.text = t.text + newT.ParseName = t.ParseName + newT.startParse(t.funcs, t.lex) + newT.parseDefinition(treeSet) + continue + } + t.backup2(delim) + } + n := t.textOrAction() + if n.Type() == nodeEnd { + t.errorf("unexpected %s", n) + } + t.Root.append(n) + } + return nil +} + +// parseDefinition parses a {{define}} ... {{end}} template definition and +// installs the definition in the treeSet map. The "define" keyword has already +// been scanned. +func (t *Tree) parseDefinition(treeSet map[string]*Tree) { + const context = "define clause" + name := t.expectOneOf(itemString, itemRawString, context) + var err error + t.Name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + var end Node + t.Root, end = t.itemList() + if end.Type() != nodeEnd { + t.errorf("unexpected %s in %s", end, context) + } + t.add(treeSet) + t.stopParse() +} + +// itemList: +// textOrAction* +// Terminates at {{end}} or {{else}}, returned separately. +func (t *Tree) itemList() (list *ListNode, next Node) { + list = t.newList(t.peekNonSpace().pos) + for t.peekNonSpace().typ != itemEOF { + n := t.textOrAction() + switch n.Type() { + case nodeEnd, nodeElse: + return list, n + } + list.append(n) + } + t.errorf("unexpected EOF") + return +} + +// textOrAction: +// text | action +func (t *Tree) textOrAction() Node { + switch token := t.nextNonSpace(); token.typ { + case itemElideNewline: + return t.elideNewline() + case itemText: + return t.newText(token.pos, token.val) + case itemLeftDelim: + return t.action() + default: + t.unexpected(token, "input") + } + return nil +} + +// elideNewline: +// Remove newlines trailing rightDelim if \\ is present. +func (t *Tree) elideNewline() Node { + token := t.peek() + if token.typ != itemText { + t.unexpected(token, "input") + return nil + } + + t.next() + stripped := strings.TrimLeft(token.val, "\n\r") + diff := len(token.val) - len(stripped) + if diff > 0 { + // This is a bit nasty. We mutate the token in-place to remove + // preceding newlines. + token.pos += Pos(diff) + token.val = stripped + } + return t.newText(token.pos, token.val) +} + +// Action: +// control +// command ("|" command)* +// Left delim is past. Now get actions. +// First word could be a keyword such as range. +func (t *Tree) action() (n Node) { + switch token := t.nextNonSpace(); token.typ { + case itemElse: + return t.elseControl() + case itemEnd: + return t.endControl() + case itemIf: + return t.ifControl() + case itemRange: + return t.rangeControl() + case itemTemplate: + return t.templateControl() + case itemWith: + return t.withControl() + } + t.backup() + // Do not pop variables; they persist until "end". + return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) +} + +// Pipeline: +// declarations? command ('|' command)* +func (t *Tree) pipeline(context string) (pipe *PipeNode) { + var decl []*VariableNode + pos := t.peekNonSpace().pos + // Are there declarations? + for { + if v := t.peekNonSpace(); v.typ == itemVariable { + t.next() + // Since space is a token, we need 3-token look-ahead here in the worst case: + // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an + // argument variable rather than a declaration. So remember the token + // adjacent to the variable so we can push it back if necessary. + tokenAfterVariable := t.peek() + if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { + t.nextNonSpace() + variable := t.newVariable(v.pos, v.val) + decl = append(decl, variable) + t.vars = append(t.vars, v.val) + if next.typ == itemChar && next.val == "," { + if context == "range" && len(decl) < 2 { + continue + } + t.errorf("too many declarations in %s", context) + } + } else if tokenAfterVariable.typ == itemSpace { + t.backup3(v, tokenAfterVariable) + } else { + t.backup2(v) + } + } + break + } + pipe = t.newPipeline(pos, t.lex.lineNumber(), decl) + for { + switch token := t.nextNonSpace(); token.typ { + case itemRightDelim, itemRightParen: + if len(pipe.Cmds) == 0 { + t.errorf("missing value for %s", context) + } + if token.typ == itemRightParen { + t.backup() + } + return + case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, + itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen: + t.backup() + pipe.append(t.command()) + default: + t.unexpected(token, context) + } + } +} + +func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { + defer t.popVars(len(t.vars)) + line = t.lex.lineNumber() + pipe = t.pipeline(context) + var next Node + list, next = t.itemList() + switch next.Type() { + case nodeEnd: //done + case nodeElse: + if allowElseIf { + // Special case for "else if". If the "else" is followed immediately by an "if", + // the elseControl will have left the "if" token pending. Treat + // {{if a}}_{{else if b}}_{{end}} + // as + // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}. + // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}} + // is assumed. This technique works even for long if-else-if chains. + // TODO: Should we allow else-if in with and range? + if t.peek().typ == itemIf { + t.next() // Consume the "if" token. + elseList = t.newList(next.Position()) + elseList.append(t.ifControl()) + // Do not consume the next item - only one {{end}} required. + break + } + } + elseList, next = t.itemList() + if next.Type() != nodeEnd { + t.errorf("expected end; found %s", next) + } + } + return pipe.Position(), line, pipe, list, elseList +} + +// If: +// {{if pipeline}} itemList {{end}} +// {{if pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Tree) ifControl() Node { + return t.newIf(t.parseControl(true, "if")) +} + +// Range: +// {{range pipeline}} itemList {{end}} +// {{range pipeline}} itemList {{else}} itemList {{end}} +// Range keyword is past. +func (t *Tree) rangeControl() Node { + return t.newRange(t.parseControl(false, "range")) +} + +// With: +// {{with pipeline}} itemList {{end}} +// {{with pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Tree) withControl() Node { + return t.newWith(t.parseControl(false, "with")) +} + +// End: +// {{end}} +// End keyword is past. +func (t *Tree) endControl() Node { + return t.newEnd(t.expect(itemRightDelim, "end").pos) +} + +// Else: +// {{else}} +// Else keyword is past. +func (t *Tree) elseControl() Node { + // Special case for "else if". + peek := t.peekNonSpace() + if peek.typ == itemIf { + // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ". + return t.newElse(peek.pos, t.lex.lineNumber()) + } + return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) +} + +// Template: +// {{template stringValue pipeline}} +// Template keyword is past. The name must be something that can evaluate +// to a string. +func (t *Tree) templateControl() Node { + var name string + token := t.nextNonSpace() + switch token.typ { + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + name = s + default: + t.unexpected(token, "template invocation") + } + var pipe *PipeNode + if t.nextNonSpace().typ != itemRightDelim { + t.backup() + // Do not pop variables; they persist until "end". + pipe = t.pipeline("template") + } + return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe) +} + +// command: +// operand (space operand)* +// space-separated arguments up to a pipeline character or right delimiter. +// we consume the pipe character but leave the right delim to terminate the action. +func (t *Tree) command() *CommandNode { + cmd := t.newCommand(t.peekNonSpace().pos) + for { + t.peekNonSpace() // skip leading spaces. + operand := t.operand() + if operand != nil { + cmd.append(operand) + } + switch token := t.next(); token.typ { + case itemSpace: + continue + case itemError: + t.errorf("%s", token.val) + case itemRightDelim, itemRightParen: + t.backup() + case itemPipe: + default: + t.errorf("unexpected %s in operand; missing space?", token) + } + break + } + if len(cmd.Args) == 0 { + t.errorf("empty command") + } + return cmd +} + +// operand: +// term .Field* +// An operand is a space-separated component of a command, +// a term possibly followed by field accesses. +// A nil return means the next item is not an operand. +func (t *Tree) operand() Node { + node := t.term() + if node == nil { + return nil + } + if t.peek().typ == itemField { + chain := t.newChain(t.peek().pos, node) + for t.peek().typ == itemField { + chain.Add(t.next().val) + } + // Compatibility with original API: If the term is of type NodeField + // or NodeVariable, just put more fields on the original. + // Otherwise, keep the Chain node. + // TODO: Switch to Chains always when we can. + switch node.Type() { + case NodeField: + node = t.newField(chain.Position(), chain.String()) + case NodeVariable: + node = t.newVariable(chain.Position(), chain.String()) + default: + node = chain + } + } + return node +} + +// term: +// literal (number, string, nil, boolean) +// function (identifier) +// . +// .Field +// $ +// '(' pipeline ')' +// A term is a simple "expression". +// A nil return means the next item is not a term. +func (t *Tree) term() Node { + switch token := t.nextNonSpace(); token.typ { + case itemError: + t.errorf("%s", token.val) + case itemIdentifier: + if !t.hasFunction(token.val) { + t.errorf("function %q not defined", token.val) + } + return NewIdentifier(token.val).SetTree(t).SetPos(token.pos) + case itemDot: + return t.newDot(token.pos) + case itemNil: + return t.newNil(token.pos) + case itemVariable: + return t.useVar(token.pos, token.val) + case itemField: + return t.newField(token.pos, token.val) + case itemBool: + return t.newBool(token.pos, token.val == "true") + case itemCharConstant, itemComplex, itemNumber: + number, err := t.newNumber(token.pos, token.val, token.typ) + if err != nil { + t.error(err) + } + return number + case itemLeftParen: + pipe := t.pipeline("parenthesized pipeline") + if token := t.next(); token.typ != itemRightParen { + t.errorf("unclosed right paren: unexpected %s", token) + } + return pipe + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + return t.newString(token.pos, token.val, s) + } + t.backup() + return nil +} + +// hasFunction reports if a function name exists in the Tree's maps. +func (t *Tree) hasFunction(name string) bool { + for _, funcMap := range t.funcs { + if funcMap == nil { + continue + } + if funcMap[name] != nil { + return true + } + } + return false +} + +// popVars trims the variable list to the specified length +func (t *Tree) popVars(n int) { + t.vars = t.vars[:n] +} + +// useVar returns a node for a variable reference. It errors if the +// variable is not defined. +func (t *Tree) useVar(pos Pos, name string) Node { + v := t.newVariable(pos, name) + for _, varName := range t.vars { + if varName == v.Ident[0] { + return v + } + } + t.errorf("undefined variable %q", v.Ident[0]) + return nil +} diff --git a/cli/vendor/github.com/alecthomas/template/template.go b/cli/vendor/github.com/alecthomas/template/template.go new file mode 100644 index 0000000..447ed2a --- /dev/null +++ b/cli/vendor/github.com/alecthomas/template/template.go @@ -0,0 +1,218 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" + "reflect" + + "github.com/alecthomas/template/parse" +) + +// common holds the information shared by related templates. +type common struct { + tmpl map[string]*Template + // We use two maps, one for parsing and one for execution. + // This separation makes the API cleaner since it doesn't + // expose reflection to the client. + parseFuncs FuncMap + execFuncs map[string]reflect.Value +} + +// Template is the representation of a parsed template. The *parse.Tree +// field is exported only for use by html/template and should be treated +// as unexported by all other clients. +type Template struct { + name string + *parse.Tree + *common + leftDelim string + rightDelim string +} + +// New allocates a new template with the given name. +func New(name string) *Template { + return &Template{ + name: name, + } +} + +// Name returns the name of the template. +func (t *Template) Name() string { + return t.name +} + +// New allocates a new template associated with the given one and with the same +// delimiters. The association, which is transitive, allows one template to +// invoke another with a {{template}} action. +func (t *Template) New(name string) *Template { + t.init() + return &Template{ + name: name, + common: t.common, + leftDelim: t.leftDelim, + rightDelim: t.rightDelim, + } +} + +func (t *Template) init() { + if t.common == nil { + t.common = new(common) + t.tmpl = make(map[string]*Template) + t.parseFuncs = make(FuncMap) + t.execFuncs = make(map[string]reflect.Value) + } +} + +// Clone returns a duplicate of the template, including all associated +// templates. The actual representation is not copied, but the name space of +// associated templates is, so further calls to Parse in the copy will add +// templates to the copy but not to the original. Clone can be used to prepare +// common templates and use them with variant definitions for other templates +// by adding the variants after the clone is made. +func (t *Template) Clone() (*Template, error) { + nt := t.copy(nil) + nt.init() + nt.tmpl[t.name] = nt + for k, v := range t.tmpl { + if k == t.name { // Already installed. + continue + } + // The associated templates share nt's common structure. + tmpl := v.copy(nt.common) + nt.tmpl[k] = tmpl + } + for k, v := range t.parseFuncs { + nt.parseFuncs[k] = v + } + for k, v := range t.execFuncs { + nt.execFuncs[k] = v + } + return nt, nil +} + +// copy returns a shallow copy of t, with common set to the argument. +func (t *Template) copy(c *common) *Template { + nt := New(t.name) + nt.Tree = t.Tree + nt.common = c + nt.leftDelim = t.leftDelim + nt.rightDelim = t.rightDelim + return nt +} + +// AddParseTree creates a new template with the name and parse tree +// and associates it with t. +func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { + if t.common != nil && t.tmpl[name] != nil { + return nil, fmt.Errorf("template: redefinition of template %q", name) + } + nt := t.New(name) + nt.Tree = tree + t.tmpl[name] = nt + return nt, nil +} + +// Templates returns a slice of the templates associated with t, including t +// itself. +func (t *Template) Templates() []*Template { + if t.common == nil { + return nil + } + // Return a slice so we don't expose the map. + m := make([]*Template, 0, len(t.tmpl)) + for _, v := range t.tmpl { + m = append(m, v) + } + return m +} + +// Delims sets the action delimiters to the specified strings, to be used in +// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template +// definitions will inherit the settings. An empty delimiter stands for the +// corresponding default: {{ or }}. +// The return value is the template, so calls can be chained. +func (t *Template) Delims(left, right string) *Template { + t.leftDelim = left + t.rightDelim = right + return t +} + +// Funcs adds the elements of the argument map to the template's function map. +// It panics if a value in the map is not a function with appropriate return +// type. However, it is legal to overwrite elements of the map. The return +// value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap FuncMap) *Template { + t.init() + addValueFuncs(t.execFuncs, funcMap) + addFuncs(t.parseFuncs, funcMap) + return t +} + +// Lookup returns the template with the given name that is associated with t, +// or nil if there is no such template. +func (t *Template) Lookup(name string) *Template { + if t.common == nil { + return nil + } + return t.tmpl[name] +} + +// Parse parses a string into a template. Nested template definitions will be +// associated with the top-level template t. Parse may be called multiple times +// to parse definitions of templates to associate with t. It is an error if a +// resulting template is non-empty (contains content other than template +// definitions) and would replace a non-empty template with the same name. +// (In multiple calls to Parse with the same receiver template, only one call +// can contain text other than space, comments, and template definitions.) +func (t *Template) Parse(text string) (*Template, error) { + t.init() + trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) + if err != nil { + return nil, err + } + // Add the newly parsed trees, including the one for t, into our common structure. + for name, tree := range trees { + // If the name we parsed is the name of this template, overwrite this template. + // The associate method checks it's not a redefinition. + tmpl := t + if name != t.name { + tmpl = t.New(name) + } + // Even if t == tmpl, we need to install it in the common.tmpl map. + if replace, err := t.associate(tmpl, tree); err != nil { + return nil, err + } else if replace { + tmpl.Tree = tree + } + tmpl.leftDelim = t.leftDelim + tmpl.rightDelim = t.rightDelim + } + return t, nil +} + +// associate installs the new template into the group of templates associated +// with t. It is an error to reuse a name except to overwrite an empty +// template. The two are already known to share the common structure. +// The boolean return value reports wither to store this tree as t.Tree. +func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { + if new.common != t.common { + panic("internal error: associate not common") + } + name := new.name + if old := t.tmpl[name]; old != nil { + oldIsEmpty := parse.IsEmptyTree(old.Root) + newIsEmpty := parse.IsEmptyTree(tree.Root) + if newIsEmpty { + // Whether old is empty or not, new is empty; no reason to replace old. + return false, nil + } + if !oldIsEmpty { + return false, fmt.Errorf("template: redefinition of template %q", name) + } + } + t.tmpl[name] = new + return true, nil +} diff --git a/cli/vendor/github.com/alecthomas/units/COPYING b/cli/vendor/github.com/alecthomas/units/COPYING new file mode 100644 index 0000000..2993ec0 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/COPYING @@ -0,0 +1,19 @@ +Copyright (C) 2014 Alec Thomas + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cli/vendor/github.com/alecthomas/units/README.md b/cli/vendor/github.com/alecthomas/units/README.md new file mode 100644 index 0000000..57b458a --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/README.md @@ -0,0 +1,13 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/alecthomas/units.svg)](https://pkg.go.dev/github.com/alecthomas/units) + +# Units - Helpful unit multipliers and functions for Go + +The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. + +It allows for code like this: + +```go +n, err := ParseBase2Bytes("1KB") +// n == 1024 +n = units.Mebibyte * 512 +``` diff --git a/cli/vendor/github.com/alecthomas/units/bytes.go b/cli/vendor/github.com/alecthomas/units/bytes.go new file mode 100644 index 0000000..2683620 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/bytes.go @@ -0,0 +1,135 @@ +package units + +// Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, +// etc.). +type Base2Bytes int64 + +// Base-2 byte units. +const ( + Kibibyte Base2Bytes = 1024 + KiB = Kibibyte + Mebibyte = Kibibyte * 1024 + MiB = Mebibyte + Gibibyte = Mebibyte * 1024 + GiB = Gibibyte + Tebibyte = Gibibyte * 1024 + TiB = Tebibyte + Pebibyte = Tebibyte * 1024 + PiB = Pebibyte + Exbibyte = Pebibyte * 1024 + EiB = Exbibyte +) + +var ( + bytesUnitMap = MakeUnitMap("iB", "B", 1024) + oldBytesUnitMap = MakeUnitMap("B", "B", 1024) +) + +// ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB +// and KiB are both 1024. +// However "kB", which is the correct SI spelling of 1000 Bytes, is rejected. +func ParseBase2Bytes(s string) (Base2Bytes, error) { + n, err := ParseUnit(s, bytesUnitMap) + if err != nil { + n, err = ParseUnit(s, oldBytesUnitMap) + } + return Base2Bytes(n), err +} + +func (b Base2Bytes) String() string { + return ToString(int64(b), 1024, "iB", "B") +} + +// MarshalText implement encoding.TextMarshaler to process json/yaml. +func (b Base2Bytes) MarshalText() ([]byte, error) { + return []byte(b.String()), nil +} + +// UnmarshalText implement encoding.TextUnmarshaler to process json/yaml. +func (b *Base2Bytes) UnmarshalText(text []byte) error { + n, err := ParseBase2Bytes(string(text)) + *b = n + return err +} + +// Floor returns Base2Bytes with all but the largest unit zeroed out. So that e.g. 1GiB1MiB1KiB → 1GiB. +func (b Base2Bytes) Floor() Base2Bytes { + switch { + case b > Exbibyte: + return (b / Exbibyte) * Exbibyte + case b > Pebibyte: + return (b / Pebibyte) * Pebibyte + case b > Tebibyte: + return (b / Tebibyte) * Tebibyte + case b > Gibibyte: + return (b / Gibibyte) * Gibibyte + case b > Mebibyte: + return (b / Mebibyte) * Mebibyte + case b > Kibibyte: + return (b / Kibibyte) * Kibibyte + default: + return b + } +} + +var metricBytesUnitMap = MakeUnitMap("B", "B", 1000) + +// MetricBytes are SI byte units (1000 bytes in a kilobyte). +type MetricBytes SI + +// SI base-10 byte units. +const ( + Kilobyte MetricBytes = 1000 + KB = Kilobyte + Megabyte = Kilobyte * 1000 + MB = Megabyte + Gigabyte = Megabyte * 1000 + GB = Gigabyte + Terabyte = Gigabyte * 1000 + TB = Terabyte + Petabyte = Terabyte * 1000 + PB = Petabyte + Exabyte = Petabyte * 1000 + EB = Exabyte +) + +// ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. +func ParseMetricBytes(s string) (MetricBytes, error) { + n, err := ParseUnit(s, metricBytesUnitMap) + return MetricBytes(n), err +} + +// TODO: represents 1000B as uppercase "KB", while SI standard requires "kB". +func (m MetricBytes) String() string { + return ToString(int64(m), 1000, "B", "B") +} + +// Floor returns MetricBytes with all but the largest unit zeroed out. So that e.g. 1GB1MB1KB → 1GB. +func (b MetricBytes) Floor() MetricBytes { + switch { + case b > Exabyte: + return (b / Exabyte) * Exabyte + case b > Petabyte: + return (b / Petabyte) * Petabyte + case b > Terabyte: + return (b / Terabyte) * Terabyte + case b > Gigabyte: + return (b / Gigabyte) * Gigabyte + case b > Megabyte: + return (b / Megabyte) * Megabyte + case b > Kilobyte: + return (b / Kilobyte) * Kilobyte + default: + return b + } +} + +// ParseStrictBytes supports both iB and B suffixes for base 2 and metric, +// respectively. That is, KiB represents 1024 and kB, KB represent 1000. +func ParseStrictBytes(s string) (int64, error) { + n, err := ParseUnit(s, bytesUnitMap) + if err != nil { + n, err = ParseUnit(s, metricBytesUnitMap) + } + return int64(n), err +} diff --git a/cli/vendor/github.com/alecthomas/units/doc.go b/cli/vendor/github.com/alecthomas/units/doc.go new file mode 100644 index 0000000..156ae38 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/doc.go @@ -0,0 +1,13 @@ +// Package units provides helpful unit multipliers and functions for Go. +// +// The goal of this package is to have functionality similar to the time [1] package. +// +// +// [1] http://golang.org/pkg/time/ +// +// It allows for code like this: +// +// n, err := ParseBase2Bytes("1KB") +// // n == 1024 +// n = units.Mebibyte * 512 +package units diff --git a/cli/vendor/github.com/alecthomas/units/si.go b/cli/vendor/github.com/alecthomas/units/si.go new file mode 100644 index 0000000..99b2fa4 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/si.go @@ -0,0 +1,50 @@ +package units + +// SI units. +type SI int64 + +// SI unit multiples. +const ( + Kilo SI = 1000 + Mega = Kilo * 1000 + Giga = Mega * 1000 + Tera = Giga * 1000 + Peta = Tera * 1000 + Exa = Peta * 1000 +) + +func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { + res := map[string]float64{ + shortSuffix: 1, + // see below for "k" / "K" + "M" + suffix: float64(scale * scale), + "G" + suffix: float64(scale * scale * scale), + "T" + suffix: float64(scale * scale * scale * scale), + "P" + suffix: float64(scale * scale * scale * scale * scale), + "E" + suffix: float64(scale * scale * scale * scale * scale * scale), + } + + // Standard SI prefixes use lowercase "k" for kilo = 1000. + // For compatibility, and to be fool-proof, we accept both "k" and "K" in metric mode. + // + // However, official binary prefixes are always capitalized - "KiB" - + // and we specifically never parse "kB" as 1024B because: + // + // (1) people pedantic enough to use lowercase according to SI unlikely to abuse "k" to mean 1024 :-) + // + // (2) Use of capital K for 1024 was an informal tradition predating IEC prefixes: + // "The binary meaning of the kilobyte for 1024 bytes typically uses the symbol KB, with an + // uppercase letter K." + // -- https://en.wikipedia.org/wiki/Kilobyte#Base_2_(1024_bytes) + // "Capitalization of the letter K became the de facto standard for binary notation, although this + // could not be extended to higher powers, and use of the lowercase k did persist.[13][14][15]" + // -- https://en.wikipedia.org/wiki/Binary_prefix#History + // See also the extensive https://en.wikipedia.org/wiki/Timeline_of_binary_prefixes. + if scale == 1024 { + res["K"+suffix] = float64(scale) + } else { + res["k"+suffix] = float64(scale) + res["K"+suffix] = float64(scale) + } + return res +} diff --git a/cli/vendor/github.com/alecthomas/units/util.go b/cli/vendor/github.com/alecthomas/units/util.go new file mode 100644 index 0000000..6527e92 --- /dev/null +++ b/cli/vendor/github.com/alecthomas/units/util.go @@ -0,0 +1,138 @@ +package units + +import ( + "errors" + "fmt" + "strings" +) + +var ( + siUnits = []string{"", "K", "M", "G", "T", "P", "E"} +) + +func ToString(n int64, scale int64, suffix, baseSuffix string) string { + mn := len(siUnits) + out := make([]string, mn) + for i, m := range siUnits { + if n%scale != 0 || i == 0 && n == 0 { + s := suffix + if i == 0 { + s = baseSuffix + } + out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) + } + n /= scale + if n == 0 { + break + } + } + return strings.Join(out, "") +} + +// Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 +var errLeadingInt = errors.New("units: bad [0-9]*") // never printed + +// leadingInt consumes the leading [0-9]* from s. +func leadingInt(s string) (x int64, rem string, err error) { + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if x >= (1<<63-10)/10 { + // overflow + return 0, "", errLeadingInt + } + x = x*10 + int64(c) - '0' + } + return x, s[i:], nil +} + +func ParseUnit(s string, unitMap map[string]float64) (int64, error) { + // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ + orig := s + f := float64(0) + neg := false + + // Consume [-+]? + if s != "" { + c := s[0] + if c == '-' || c == '+' { + neg = c == '-' + s = s[1:] + } + } + // Special case: if all that is left is "0", this is zero. + if s == "0" { + return 0, nil + } + if s == "" { + return 0, errors.New("units: invalid " + orig) + } + for s != "" { + g := float64(0) // this element of the sequence + + var x int64 + var err error + + // The next character must be [0-9.] + if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { + return 0, errors.New("units: invalid " + orig) + } + // Consume [0-9]* + pl := len(s) + x, s, err = leadingInt(s) + if err != nil { + return 0, errors.New("units: invalid " + orig) + } + g = float64(x) + pre := pl != len(s) // whether we consumed anything before a period + + // Consume (\.[0-9]*)? + post := false + if s != "" && s[0] == '.' { + s = s[1:] + pl := len(s) + x, s, err = leadingInt(s) + if err != nil { + return 0, errors.New("units: invalid " + orig) + } + scale := 1.0 + for n := pl - len(s); n > 0; n-- { + scale *= 10 + } + g += float64(x) / scale + post = pl != len(s) + } + if !pre && !post { + // no digits (e.g. ".s" or "-.s") + return 0, errors.New("units: invalid " + orig) + } + + // Consume unit. + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c == '.' || ('0' <= c && c <= '9') { + break + } + } + u := s[:i] + s = s[i:] + unit, ok := unitMap[u] + if !ok { + return 0, errors.New("units: unknown unit " + u + " in " + orig) + } + + f += g * unit + } + + if neg { + f = -f + } + if f < float64(-1<<63) || f > float64(1<<63-1) { + return 0, errors.New("units: overflow parsing unit") + } + return int64(f), nil +} diff --git a/cli/vendor/github.com/fatih/structs/.gitignore b/cli/vendor/github.com/fatih/structs/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/cli/vendor/github.com/fatih/structs/.travis.yml b/cli/vendor/github.com/fatih/structs/.travis.yml new file mode 100644 index 0000000..a08df79 --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/.travis.yml @@ -0,0 +1,13 @@ +language: go +go: + - 1.7.x + - 1.8.x + - 1.9.x + - tip +sudo: false +before_install: +- go get github.com/axw/gocov/gocov +- go get github.com/mattn/goveralls +- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi +script: +- $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/cli/vendor/github.com/fatih/structs/LICENSE b/cli/vendor/github.com/fatih/structs/LICENSE new file mode 100644 index 0000000..34504e4 --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 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. \ No newline at end of file diff --git a/cli/vendor/github.com/fatih/structs/README.md b/cli/vendor/github.com/fatih/structs/README.md new file mode 100644 index 0000000..a75eabf --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/README.md @@ -0,0 +1,163 @@ +# Structs [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structs) [![Build Status](http://img.shields.io/travis/fatih/structs.svg?style=flat-square)](https://travis-ci.org/fatih/structs) [![Coverage Status](http://img.shields.io/coveralls/fatih/structs.svg?style=flat-square)](https://coveralls.io/r/fatih/structs) + +Structs contains various utilities to work with Go (Golang) structs. It was +initially used by me to convert a struct into a `map[string]interface{}`. With +time I've added other utilities for structs. It's basically a high level +package based on primitives from the reflect package. Feel free to add new +functions or improve the existing code. + +## Install + +```bash +go get github.com/fatih/structs +``` + +## Usage and Examples + +Just like the standard lib `strings`, `bytes` and co packages, `structs` has +many global functions to manipulate or organize your struct data. Lets define +and declare a struct: + +```go +type Server struct { + Name string `json:"name,omitempty"` + ID int + Enabled bool + users []string // not exported + http.Server // embedded +} + +server := &Server{ + Name: "gopher", + ID: 123456, + Enabled: true, +} +``` + +```go +// Convert a struct to a map[string]interface{} +// => {"Name":"gopher", "ID":123456, "Enabled":true} +m := structs.Map(server) + +// Convert the values of a struct to a []interface{} +// => ["gopher", 123456, true] +v := structs.Values(server) + +// Convert the names of a struct to a []string +// (see "Names methods" for more info about fields) +n := structs.Names(server) + +// Convert the values of a struct to a []*Field +// (see "Field methods" for more info about fields) +f := structs.Fields(server) + +// Return the struct name => "Server" +n := structs.Name(server) + +// Check if any field of a struct is initialized or not. +h := structs.HasZero(server) + +// Check if all fields of a struct is initialized or not. +z := structs.IsZero(server) + +// Check if server is a struct or a pointer to struct +i := structs.IsStruct(server) +``` + +### Struct methods + +The structs functions can be also used as independent methods by creating a new +`*structs.Struct`. This is handy if you want to have more control over the +structs (such as retrieving a single Field). + +```go +// Create a new struct type: +s := structs.New(server) + +m := s.Map() // Get a map[string]interface{} +v := s.Values() // Get a []interface{} +f := s.Fields() // Get a []*Field +n := s.Names() // Get a []string +f := s.Field(name) // Get a *Field based on the given field name +f, ok := s.FieldOk(name) // Get a *Field based on the given field name +n := s.Name() // Get the struct name +h := s.HasZero() // Check if any field is uninitialized +z := s.IsZero() // Check if all fields are uninitialized +``` + +### Field methods + +We can easily examine a single Field for more detail. Below you can see how we +get and interact with various field methods: + + +```go +s := structs.New(server) + +// Get the Field struct for the "Name" field +name := s.Field("Name") + +// Get the underlying value, value => "gopher" +value := name.Value().(string) + +// Set the field's value +name.Set("another gopher") + +// Get the field's kind, kind => "string" +name.Kind() + +// Check if the field is exported or not +if name.IsExported() { + fmt.Println("Name field is exported") +} + +// Check if the value is a zero value, such as "" for string, 0 for int +if !name.IsZero() { + fmt.Println("Name is initialized") +} + +// Check if the field is an anonymous (embedded) field +if !name.IsEmbedded() { + fmt.Println("Name is not an embedded field") +} + +// Get the Field's tag value for tag name "json", tag value => "name,omitempty" +tagValue := name.Tag("json") +``` + +Nested structs are supported too: + +```go +addrField := s.Field("Server").Field("Addr") + +// Get the value for addr +a := addrField.Value().(string) + +// Or get all fields +httpServer := s.Field("Server").Fields() +``` + +We can also get a slice of Fields from the Struct type to iterate over all +fields. This is handy if you wish to examine all fields: + +```go +s := structs.New(server) + +for _, f := range s.Fields() { + fmt.Printf("field name: %+v\n", f.Name()) + + if f.IsExported() { + fmt.Printf("value : %+v\n", f.Value()) + fmt.Printf("is zero : %+v\n", f.IsZero()) + } +} +``` + +## Credits + + * [Fatih Arslan](https://github.com/fatih) + * [Cihangir Savas](https://github.com/cihangir) + +## License + +The MIT License (MIT) - see LICENSE.md for more details diff --git a/cli/vendor/github.com/fatih/structs/field.go b/cli/vendor/github.com/fatih/structs/field.go new file mode 100644 index 0000000..e697832 --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/field.go @@ -0,0 +1,141 @@ +package structs + +import ( + "errors" + "fmt" + "reflect" +) + +var ( + errNotExported = errors.New("field is not exported") + errNotSettable = errors.New("field is not settable") +) + +// Field represents a single struct field that encapsulates high level +// functions around the field. +type Field struct { + value reflect.Value + field reflect.StructField + defaultTag string +} + +// Tag returns the value associated with key in the tag string. If there is no +// such key in the tag, Tag returns the empty string. +func (f *Field) Tag(key string) string { + return f.field.Tag.Get(key) +} + +// Value returns the underlying value of the field. It panics if the field +// is not exported. +func (f *Field) Value() interface{} { + return f.value.Interface() +} + +// IsEmbedded returns true if the given field is an anonymous field (embedded) +func (f *Field) IsEmbedded() bool { + return f.field.Anonymous +} + +// IsExported returns true if the given field is exported. +func (f *Field) IsExported() bool { + return f.field.PkgPath == "" +} + +// IsZero returns true if the given field is not initialized (has a zero value). +// It panics if the field is not exported. +func (f *Field) IsZero() bool { + zero := reflect.Zero(f.value.Type()).Interface() + current := f.Value() + + return reflect.DeepEqual(current, zero) +} + +// Name returns the name of the given field +func (f *Field) Name() string { + return f.field.Name +} + +// Kind returns the fields kind, such as "string", "map", "bool", etc .. +func (f *Field) Kind() reflect.Kind { + return f.value.Kind() +} + +// Set sets the field to given value v. It returns an error if the field is not +// settable (not addressable or not exported) or if the given value's type +// doesn't match the fields type. +func (f *Field) Set(val interface{}) error { + // we can't set unexported fields, so be sure this field is exported + if !f.IsExported() { + return errNotExported + } + + // do we get here? not sure... + if !f.value.CanSet() { + return errNotSettable + } + + given := reflect.ValueOf(val) + + if f.value.Kind() != given.Kind() { + return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind()) + } + + f.value.Set(given) + return nil +} + +// Zero sets the field to its zero value. It returns an error if the field is not +// settable (not addressable or not exported). +func (f *Field) Zero() error { + zero := reflect.Zero(f.value.Type()).Interface() + return f.Set(zero) +} + +// Fields returns a slice of Fields. This is particular handy to get the fields +// of a nested struct . A struct tag with the content of "-" ignores the +// checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field *http.Request `structs:"-"` +// +// It panics if field is not exported or if field's kind is not struct +func (f *Field) Fields() []*Field { + return getFields(f.value, f.defaultTag) +} + +// Field returns the field from a nested struct. It panics if the nested struct +// is not exported or if the field was not found. +func (f *Field) Field(name string) *Field { + field, ok := f.FieldOk(name) + if !ok { + panic("field not found") + } + + return field +} + +// FieldOk returns the field from a nested struct. The boolean returns whether +// the field was found (true) or not (false). +func (f *Field) FieldOk(name string) (*Field, bool) { + value := &f.value + // value must be settable so we need to make sure it holds the address of the + // variable and not a copy, so we can pass the pointer to strctVal instead of a + // copy (which is not assigned to any variable, hence not settable). + // see "https://blog.golang.org/laws-of-reflection#TOC_8." + if f.value.Kind() != reflect.Ptr { + a := f.value.Addr() + value = &a + } + v := strctVal(value.Interface()) + t := v.Type() + + field, ok := t.FieldByName(name) + if !ok { + return nil, false + } + + return &Field{ + field: field, + value: v.FieldByName(name), + }, true +} diff --git a/cli/vendor/github.com/fatih/structs/structs.go b/cli/vendor/github.com/fatih/structs/structs.go new file mode 100644 index 0000000..3a87706 --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/structs.go @@ -0,0 +1,584 @@ +// Package structs contains various utilities functions to work with structs. +package structs + +import ( + "fmt" + + "reflect" +) + +var ( + // DefaultTagName is the default tag name for struct fields which provides + // a more granular to tweak certain structs. Lookup the necessary functions + // for more info. + DefaultTagName = "structs" // struct's field default tag name +) + +// Struct encapsulates a struct type to provide several high level functions +// around the struct. +type Struct struct { + raw interface{} + value reflect.Value + TagName string +} + +// New returns a new *Struct with the struct s. It panics if the s's kind is +// not struct. +func New(s interface{}) *Struct { + return &Struct{ + raw: s, + value: strctVal(s), + TagName: DefaultTagName, + } +} + +// Map converts the given struct to a map[string]interface{}, where the keys +// of the map are the field names and the values of the map the associated +// values of the fields. The default key string is the struct field name but +// can be changed in the struct field's tag value. The "structs" key in the +// struct's field tag value is the key name. Example: +// +// // Field appears in map as key "myName". +// Name string `structs:"myName"` +// +// A tag value with the content of "-" ignores that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A tag value with the content of "string" uses the stringer to get the value. Example: +// +// // The value will be output of Animal's String() func. +// // Map will panic if Animal does not implement String(). +// Field *Animal `structs:"field,string"` +// +// A tag value with the option of "flatten" used in a struct field is to flatten its fields +// in the output map. Example: +// +// // The FieldStruct's fields will be flattened into the output map. +// FieldStruct time.Time `structs:",flatten"` +// +// A tag value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// A tag value with the option of "omitempty" ignores that particular field if +// the field value is empty. Example: +// +// // Field appears in map as key "myName", but the field is +// // skipped if empty. +// Field string `structs:"myName,omitempty"` +// +// // Field appears in map as key "Field" (the default), but +// // the field is skipped if empty. +// Field string `structs:",omitempty"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. +func (s *Struct) Map() map[string]interface{} { + out := make(map[string]interface{}) + s.FillMap(out) + return out +} + +// FillMap is the same as Map. Instead of returning the output, it fills the +// given map. +func (s *Struct) FillMap(out map[string]interface{}) { + if out == nil { + return + } + + fields := s.structFields() + + for _, field := range fields { + name := field.Name + val := s.value.FieldByName(name) + isSubStruct := false + var finalVal interface{} + + tagName, tagOpts := parseTag(field.Tag.Get(s.TagName)) + if tagName != "" { + name = tagName + } + + // if the value is a zero value and the field is marked as omitempty do + // not include + if tagOpts.Has("omitempty") { + zero := reflect.Zero(val.Type()).Interface() + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + continue + } + } + + if !tagOpts.Has("omitnested") { + finalVal = s.nested(val) + + v := reflect.ValueOf(val.Interface()) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + switch v.Kind() { + case reflect.Map, reflect.Struct: + isSubStruct = true + } + } else { + finalVal = val.Interface() + } + + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + out[name] = s.String() + } + continue + } + + if isSubStruct && (tagOpts.Has("flatten")) { + for k := range finalVal.(map[string]interface{}) { + out[k] = finalVal.(map[string]interface{})[k] + } + } else { + out[name] = finalVal + } + } +} + +// Values converts the given s struct's field values to a []interface{}. A +// struct tag with the content of "-" ignores the that particular field. +// Example: +// +// // Field is ignored by this package. +// Field int `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Fields is not processed further by this package. +// Field time.Time `structs:",omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// A tag value with the option of "omitempty" ignores that particular field and +// is not added to the values if the field value is empty. Example: +// +// // Field is skipped if empty +// Field string `structs:",omitempty"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. +func (s *Struct) Values() []interface{} { + fields := s.structFields() + + var t []interface{} + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + // if the value is a zero value and the field is marked as omitempty do + // not include + if tagOpts.Has("omitempty") { + zero := reflect.Zero(val.Type()).Interface() + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + continue + } + } + + if tagOpts.Has("string") { + s, ok := val.Interface().(fmt.Stringer) + if ok { + t = append(t, s.String()) + } + continue + } + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + // look out for embedded structs, and convert them to a + // []interface{} to be added to the final values slice + t = append(t, Values(val.Interface())...) + } else { + t = append(t, val.Interface()) + } + } + + return t +} + +// Fields returns a slice of Fields. A struct tag with the content of "-" +// ignores the checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// It panics if s's kind is not struct. +func (s *Struct) Fields() []*Field { + return getFields(s.value, s.TagName) +} + +// Names returns a slice of field names. A struct tag with the content of "-" +// ignores the checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// It panics if s's kind is not struct. +func (s *Struct) Names() []string { + fields := getFields(s.value, s.TagName) + + names := make([]string, len(fields)) + + for i, field := range fields { + names[i] = field.Name() + } + + return names +} + +func getFields(v reflect.Value, tagName string) []*Field { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + t := v.Type() + + var fields []*Field + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + + if tag := field.Tag.Get(tagName); tag == "-" { + continue + } + + f := &Field{ + field: field, + value: v.FieldByName(field.Name), + } + + fields = append(fields, f) + + } + + return fields +} + +// Field returns a new Field struct that provides several high level functions +// around a single struct field entity. It panics if the field is not found. +func (s *Struct) Field(name string) *Field { + f, ok := s.FieldOk(name) + if !ok { + panic("field not found") + } + + return f +} + +// FieldOk returns a new Field struct that provides several high level functions +// around a single struct field entity. The boolean returns true if the field +// was found. +func (s *Struct) FieldOk(name string) (*Field, bool) { + t := s.value.Type() + + field, ok := t.FieldByName(name) + if !ok { + return nil, false + } + + return &Field{ + field: field, + value: s.value.FieldByName(name), + defaultTag: s.TagName, + }, true +} + +// IsZero returns true if all fields in a struct is a zero value (not +// initialized) A struct tag with the content of "-" ignores the checking of +// that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. It panics if s's kind is not struct. +func (s *Struct) IsZero() bool { + fields := s.structFields() + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + ok := IsZero(val.Interface()) + if !ok { + return false + } + + continue + } + + // zero value of the given field, such as "" for string, 0 for int + zero := reflect.Zero(val.Type()).Interface() + + // current value of the given field + current := val.Interface() + + if !reflect.DeepEqual(current, zero) { + return false + } + } + + return true +} + +// HasZero returns true if a field in a struct is not initialized (zero value). +// A struct tag with the content of "-" ignores the checking of that particular +// field. Example: +// +// // Field is ignored by this package. +// Field bool `structs:"-"` +// +// A value with the option of "omitnested" stops iterating further if the type +// is a struct. Example: +// +// // Field is not processed further by this package. +// Field time.Time `structs:"myName,omitnested"` +// Field *http.Request `structs:",omitnested"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. It panics if s's kind is not struct. +func (s *Struct) HasZero() bool { + fields := s.structFields() + + for _, field := range fields { + val := s.value.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(s.TagName)) + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + ok := HasZero(val.Interface()) + if ok { + return true + } + + continue + } + + // zero value of the given field, such as "" for string, 0 for int + zero := reflect.Zero(val.Type()).Interface() + + // current value of the given field + current := val.Interface() + + if reflect.DeepEqual(current, zero) { + return true + } + } + + return false +} + +// Name returns the structs's type name within its package. For more info refer +// to Name() function. +func (s *Struct) Name() string { + return s.value.Type().Name() +} + +// structFields returns the exported struct fields for a given s struct. This +// is a convenient helper method to avoid duplicate code in some of the +// functions. +func (s *Struct) structFields() []reflect.StructField { + t := s.value.Type() + + var f []reflect.StructField + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + // we can't access the value of unexported fields + if field.PkgPath != "" { + continue + } + + // don't check if it's omitted + if tag := field.Tag.Get(s.TagName); tag == "-" { + continue + } + + f = append(f, field) + } + + return f +} + +func strctVal(s interface{}) reflect.Value { + v := reflect.ValueOf(s) + + // if pointer get the underlying element≤ + for v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if v.Kind() != reflect.Struct { + panic("not struct") + } + + return v +} + +// Map converts the given struct to a map[string]interface{}. For more info +// refer to Struct types Map() method. It panics if s's kind is not struct. +func Map(s interface{}) map[string]interface{} { + return New(s).Map() +} + +// FillMap is the same as Map. Instead of returning the output, it fills the +// given map. +func FillMap(s interface{}, out map[string]interface{}) { + New(s).FillMap(out) +} + +// Values converts the given struct to a []interface{}. For more info refer to +// Struct types Values() method. It panics if s's kind is not struct. +func Values(s interface{}) []interface{} { + return New(s).Values() +} + +// Fields returns a slice of *Field. For more info refer to Struct types +// Fields() method. It panics if s's kind is not struct. +func Fields(s interface{}) []*Field { + return New(s).Fields() +} + +// Names returns a slice of field names. For more info refer to Struct types +// Names() method. It panics if s's kind is not struct. +func Names(s interface{}) []string { + return New(s).Names() +} + +// IsZero returns true if all fields is equal to a zero value. For more info +// refer to Struct types IsZero() method. It panics if s's kind is not struct. +func IsZero(s interface{}) bool { + return New(s).IsZero() +} + +// HasZero returns true if any field is equal to a zero value. For more info +// refer to Struct types HasZero() method. It panics if s's kind is not struct. +func HasZero(s interface{}) bool { + return New(s).HasZero() +} + +// IsStruct returns true if the given variable is a struct or a pointer to +// struct. +func IsStruct(s interface{}) bool { + v := reflect.ValueOf(s) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + // uninitialized zero value of a struct + if v.Kind() == reflect.Invalid { + return false + } + + return v.Kind() == reflect.Struct +} + +// Name returns the structs's type name within its package. It returns an +// empty string for unnamed types. It panics if s's kind is not struct. +func Name(s interface{}) string { + return New(s).Name() +} + +// nested retrieves recursively all types for the given value and returns the +// nested value. +func (s *Struct) nested(val reflect.Value) interface{} { + var finalVal interface{} + + v := reflect.ValueOf(val.Interface()) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + switch v.Kind() { + case reflect.Struct: + n := New(val.Interface()) + n.TagName = s.TagName + m := n.Map() + + // do not add the converted value if there are no exported fields, ie: + // time.Time + if len(m) == 0 { + finalVal = val.Interface() + } else { + finalVal = m + } + case reflect.Map: + // get the element type of the map + mapElem := val.Type() + switch val.Type().Kind() { + case reflect.Ptr, reflect.Array, reflect.Map, + reflect.Slice, reflect.Chan: + mapElem = val.Type().Elem() + if mapElem.Kind() == reflect.Ptr { + mapElem = mapElem.Elem() + } + } + + // only iterate over struct types, ie: map[string]StructType, + // map[string][]StructType, + if mapElem.Kind() == reflect.Struct || + (mapElem.Kind() == reflect.Slice && + mapElem.Elem().Kind() == reflect.Struct) { + m := make(map[string]interface{}, val.Len()) + for _, k := range val.MapKeys() { + m[k.String()] = s.nested(val.MapIndex(k)) + } + finalVal = m + break + } + + // TODO(arslan): should this be optional? + finalVal = val.Interface() + case reflect.Slice, reflect.Array: + if val.Type().Kind() == reflect.Interface { + finalVal = val.Interface() + break + } + + // TODO(arslan): should this be optional? + // do not iterate of non struct types, just pass the value. Ie: []int, + // []string, co... We only iterate further if it's a struct. + // i.e []foo or []*foo + if val.Type().Elem().Kind() != reflect.Struct && + !(val.Type().Elem().Kind() == reflect.Ptr && + val.Type().Elem().Elem().Kind() == reflect.Struct) { + finalVal = val.Interface() + break + } + + slices := make([]interface{}, val.Len()) + for x := 0; x < val.Len(); x++ { + slices[x] = s.nested(val.Index(x)) + } + finalVal = slices + default: + finalVal = val.Interface() + } + + return finalVal +} diff --git a/cli/vendor/github.com/fatih/structs/tags.go b/cli/vendor/github.com/fatih/structs/tags.go new file mode 100644 index 0000000..136a31e --- /dev/null +++ b/cli/vendor/github.com/fatih/structs/tags.go @@ -0,0 +1,32 @@ +package structs + +import "strings" + +// tagOptions contains a slice of tag options +type tagOptions []string + +// Has returns true if the given option is available in tagOptions +func (t tagOptions) Has(opt string) bool { + for _, tagOpt := range t { + if tagOpt == opt { + return true + } + } + + return false +} + +// parseTag splits a struct field's tag into its name and a list of options +// which comes after a name. A tag is in the form of: "name,option1,option2". +// The name can be neglectected. +func parseTag(tag string) (string, tagOptions) { + // tag is one of followings: + // "" + // "name" + // "name,opt" + // "name,opt,opt2" + // ",opt" + + res := strings.Split(tag, ",") + return res[0], res[1:] +} diff --git a/cli/vendor/github.com/go-ole/go-ole/.travis.yml b/cli/vendor/github.com/go-ole/go-ole/.travis.yml new file mode 100644 index 0000000..28f740c --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/.travis.yml @@ -0,0 +1,8 @@ +language: go +sudo: false + +go: + - 1.9.x + - 1.10.x + - 1.11.x + - tip diff --git a/cli/vendor/github.com/go-ole/go-ole/ChangeLog.md b/cli/vendor/github.com/go-ole/go-ole/ChangeLog.md new file mode 100644 index 0000000..4ba6a8c --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/ChangeLog.md @@ -0,0 +1,49 @@ +# Version 1.x.x + +* **Add more test cases and reference new test COM server project.** (Placeholder for future additions) + +# Version 1.2.0-alphaX + +**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.** + + * Added CI configuration for Travis-CI and AppVeyor. + * Added test InterfaceID and ClassID for the COM Test Server project. + * Added more inline documentation (#83). + * Added IEnumVARIANT implementation (#88). + * Added IEnumVARIANT test cases (#99, #100, #101). + * Added support for retrieving `time.Time` from VARIANT (#92). + * Added test case for IUnknown (#64). + * Added test case for IDispatch (#64). + * Added test cases for scalar variants (#64, #76). + +# Version 1.1.1 + + * Fixes for Linux build. + * Fixes for Windows build. + +# Version 1.1.0 + +The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes. + + * Move GUID out of variables.go into its own file to make new documentation available. + * Move OleError out of ole.go into its own file to make new documentation available. + * Add documentation to utility functions. + * Add documentation to variant receiver functions. + * Add documentation to ole structures. + * Make variant available to other systems outside of Windows. + * Make OLE structures available to other systems outside of Windows. + +## New Features + + * Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows. + * More functions are now documented and available on godoc.org. + +# Version 1.0.1 + + 1. Fix package references from repository location change. + +# Version 1.0.0 + +This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface. + +There is no changelog for this version. Check commits for history. diff --git a/cli/vendor/github.com/go-ole/go-ole/LICENSE b/cli/vendor/github.com/go-ole/go-ole/LICENSE new file mode 100644 index 0000000..623ec06 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2013-2017 Yasuhiro Matsumoto, + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cli/vendor/github.com/go-ole/go-ole/README.md b/cli/vendor/github.com/go-ole/go-ole/README.md new file mode 100644 index 0000000..7b57755 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/README.md @@ -0,0 +1,46 @@ +# Go OLE + +[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28) +[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole) +[![GoDoc](https://godoc.org/github.com/go-ole/go-ole?status.svg)](https://godoc.org/github.com/go-ole/go-ole) + +Go bindings for Windows COM using shared libraries instead of cgo. + +By Yasuhiro Matsumoto. + +## Install + +To experiment with go-ole, you can just compile and run the example program: + +``` +go get github.com/go-ole/go-ole +cd /path/to/go-ole/ +go test + +cd /path/to/go-ole/example/excel +go run excel.go +``` + +## Continuous Integration + +Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run. + +**Travis-CI** + +Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server. + +**AppVeyor** + +AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server. + +The tests currently do run and do pass and this should be maintained with commits. + +## Versioning + +Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch. + +This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed. + +## LICENSE + +Under the MIT License: http://mattn.mit-license.org/2013 diff --git a/cli/vendor/github.com/go-ole/go-ole/appveyor.yml b/cli/vendor/github.com/go-ole/go-ole/appveyor.yml new file mode 100644 index 0000000..0d557ac --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/appveyor.yml @@ -0,0 +1,54 @@ +# Notes: +# - Minimal appveyor.yml file is an empty file. All sections are optional. +# - Indent each level of configuration with 2 spaces. Do not use tabs! +# - All section names are case-sensitive. +# - Section names should be unique on each level. + +version: "1.3.0.{build}-alpha-{branch}" + +os: Windows Server 2012 R2 + +branches: + only: + - master + - v1.2 + - v1.1 + - v1.0 + +skip_tags: true + +clone_folder: c:\gopath\src\github.com\go-ole\go-ole + +environment: + GOPATH: c:\gopath + matrix: + - GOARCH: amd64 + GOVERSION: 1.5 + GOROOT: c:\go + DOWNLOADPLATFORM: "x64" + +install: + - choco install mingw + - SET PATH=c:\tools\mingw64\bin;%PATH% + # - Download COM Server + - ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip" + - 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL + - c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat + # - set + - go version + - go env + - go get -u golang.org/x/tools/cmd/cover + - go get -u golang.org/x/tools/cmd/godoc + - go get -u golang.org/x/tools/cmd/stringer + +build_script: + - cd c:\gopath\src\github.com\go-ole\go-ole + - go get -v -t ./... + - go build + - go test -v -cover ./... + +# disable automatic tests +test: off + +# disable deployment +deploy: off diff --git a/cli/vendor/github.com/go-ole/go-ole/com.go b/cli/vendor/github.com/go-ole/go-ole/com.go new file mode 100644 index 0000000..6f986b1 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/com.go @@ -0,0 +1,344 @@ +// +build windows + +package ole + +import ( + "syscall" + "unicode/utf16" + "unsafe" +) + +var ( + procCoInitialize, _ = modole32.FindProc("CoInitialize") + procCoInitializeEx, _ = modole32.FindProc("CoInitializeEx") + procCoUninitialize, _ = modole32.FindProc("CoUninitialize") + procCoCreateInstance, _ = modole32.FindProc("CoCreateInstance") + procCoTaskMemFree, _ = modole32.FindProc("CoTaskMemFree") + procCLSIDFromProgID, _ = modole32.FindProc("CLSIDFromProgID") + procCLSIDFromString, _ = modole32.FindProc("CLSIDFromString") + procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID") + procStringFromIID, _ = modole32.FindProc("StringFromIID") + procIIDFromString, _ = modole32.FindProc("IIDFromString") + procCoGetObject, _ = modole32.FindProc("CoGetObject") + procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID") + procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory") + procVariantInit, _ = modoleaut32.FindProc("VariantInit") + procVariantClear, _ = modoleaut32.FindProc("VariantClear") + procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime") + procSysAllocString, _ = modoleaut32.FindProc("SysAllocString") + procSysAllocStringLen, _ = modoleaut32.FindProc("SysAllocStringLen") + procSysFreeString, _ = modoleaut32.FindProc("SysFreeString") + procSysStringLen, _ = modoleaut32.FindProc("SysStringLen") + procCreateDispTypeInfo, _ = modoleaut32.FindProc("CreateDispTypeInfo") + procCreateStdDispatch, _ = modoleaut32.FindProc("CreateStdDispatch") + procGetActiveObject, _ = modoleaut32.FindProc("GetActiveObject") + + procGetMessageW, _ = moduser32.FindProc("GetMessageW") + procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW") +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx + // Suggests that no value should be passed to CoInitialized. + // Could just be Call() since the parameter is optional. <-- Needs testing to be sure. + hr, _, _ := procCoInitialize.Call(uintptr(0)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) (err error) { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx + // Suggests that the first parameter is not only optional but should always be NULL. + hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) (err error) { + // p is ignored and won't be used. + // Avoid any variable not used errors. + p = uintptr(0) + return coInitialize() +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) (err error) { + // Avoid any variable not used errors. + p = uintptr(0) + return coInitializeEx(coinit) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() { + procCoUninitialize.Call() +} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) { + procCoTaskMemFree.Call(memptr) +} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (clsid *GUID, err error) { + var guid GUID + lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) + hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (clsid *GUID, err error) { + var guid GUID + lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) + hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) + if hr != 0 { + err = NewError(hr) + } + clsid = &guid + return +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (str string, err error) { + var p *uint16 + hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p))) + if hr != 0 { + err = NewError(hr) + } + str = LpOleStrToString(p) + return +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoCreateInstance.Call( + uintptr(unsafe.Pointer(clsid)), + 0, + CLSCTX_SERVER, + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procGetActiveObject.Call( + uintptr(unsafe.Pointer(clsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +type BindOpts struct { + CbStruct uint32 + GrfFlags uint32 + GrfMode uint32 + TickCountDeadline uint32 +} + +// GetObject retrieves pointer to active object. +func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) { + if bindOpts != nil { + bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{})) + } + if iid == nil { + iid = IID_IUnknown + } + hr, _, _ := procCoGetObject.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))), + uintptr(unsafe.Pointer(bindOpts)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&unk))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) (err error) { + hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) (err error) { + hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) (ss *int16) { + pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v)))) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) (ss *int16) { + utf16 := utf16.Encode([]rune(v + "\x00")) + ptr := &utf16[0] + + pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1)) + ss = (*int16)(unsafe.Pointer(pss)) + return +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) (err error) { + hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v))) + return uint32(l) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) { + hr, _, _ := procCreateStdDispatch.Call( + uintptr(unsafe.Pointer(unk)), + v, + uintptr(unsafe.Pointer(ptinfo)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) { + hr, _, _ := procCreateDispTypeInfo.Call( + uintptr(unsafe.Pointer(idata)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&pptinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) { + procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length)) +} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() (lcid uint32) { + ret, _, _ := procGetUserDefaultLCID.Call() + lcid = uint32(ret) + return +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) { + r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax)) + ret = int32(r0) + return +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) (ret int32) { + r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg))) + ret = int32(r0) + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/com_func.go b/cli/vendor/github.com/go-ole/go-ole/com_func.go new file mode 100644 index 0000000..cef539d --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/com_func.go @@ -0,0 +1,174 @@ +// +build !windows + +package ole + +import ( + "time" + "unsafe" +) + +// coInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func coInitialize() error { + return NewError(E_NOTIMPL) +} + +// coInitializeEx initializes COM library with concurrency model. +func coInitializeEx(coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoInitialize initializes COM library on current thread. +// +// MSDN documentation suggests that this function should not be called. Call +// CoInitializeEx() instead. The reason has to do with threading and this +// function is only for single-threaded apartments. +// +// That said, most users of the library have gotten away with just this +// function. If you are experiencing threading issues, then use +// CoInitializeEx(). +func CoInitialize(p uintptr) error { + return NewError(E_NOTIMPL) +} + +// CoInitializeEx initializes COM library with concurrency model. +func CoInitializeEx(p uintptr, coinit uint32) error { + return NewError(E_NOTIMPL) +} + +// CoUninitialize uninitializes COM Library. +func CoUninitialize() {} + +// CoTaskMemFree frees memory pointer. +func CoTaskMemFree(memptr uintptr) {} + +// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. +// +// The Programmatic Identifier must be registered, because it will be looked up +// in the Windows Registry. The registry entry has the following keys: CLSID, +// Insertable, Protocol and Shell +// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). +// +// programID identifies the class id with less precision and is not guaranteed +// to be unique. These are usually found in the registry under +// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of +// "Program.Component.Version" with version being optional. +// +// CLSIDFromProgID in Windows API. +func CLSIDFromProgID(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// CLSIDFromString retrieves Class ID from string representation. +// +// This is technically the string version of the GUID and will convert the +// string to object. +// +// CLSIDFromString in Windows API. +func CLSIDFromString(str string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromCLSID returns GUID formated string from GUID object. +func StringFromCLSID(clsid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// IIDFromString returns GUID from program ID. +func IIDFromString(progId string) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// StringFromIID returns GUID formatted string from GUID object. +func StringFromIID(iid *GUID) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// CreateInstance of single uninitialized object with GUID. +func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// GetActiveObject retrieves pointer to active object. +func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// VariantInit initializes variant. +func VariantInit(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// VariantClear clears value in Variant settings to VT_EMPTY. +func VariantClear(v *VARIANT) error { + return NewError(E_NOTIMPL) +} + +// SysAllocString allocates memory for string and copies string into memory. +func SysAllocString(v string) *int16 { + u := int16(0) + return &u +} + +// SysAllocStringLen copies up to length of given string returning pointer. +func SysAllocStringLen(v string) *int16 { + u := int16(0) + return &u +} + +// SysFreeString frees string system memory. This must be called with SysAllocString. +func SysFreeString(v *int16) error { + return NewError(E_NOTIMPL) +} + +// SysStringLen is the length of the system allocated string. +func SysStringLen(v *int16) uint32 { + return uint32(0) +} + +// CreateStdDispatch provides default IDispatch implementation for IUnknown. +// +// This handles default IDispatch implementation for objects. It haves a few +// limitations with only supporting one language. It will also only return +// default exception codes. +func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) { + return nil, NewError(E_NOTIMPL) +} + +// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. +// +// This will not handle the full implementation of the interface. +func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) { + return nil, NewError(E_NOTIMPL) +} + +// copyMemory moves location of a block of memory. +func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {} + +// GetUserDefaultLCID retrieves current user default locale. +func GetUserDefaultLCID() uint32 { + return uint32(0) +} + +// GetMessage in message queue from runtime. +// +// This function appears to block. PeekMessage does not block. +func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// DispatchMessage to window procedure. +func DispatchMessage(msg *Msg) int32 { + return int32(0) +} + +func GetVariantDate(value uint64) (time.Time, error) { + return time.Now(), NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/connect.go b/cli/vendor/github.com/go-ole/go-ole/connect.go new file mode 100644 index 0000000..b2ac2ec --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/connect.go @@ -0,0 +1,192 @@ +package ole + +// Connection contains IUnknown for fluent interface interaction. +// +// Deprecated. Use oleutil package instead. +type Connection struct { + Object *IUnknown // Access COM +} + +// Initialize COM. +func (*Connection) Initialize() (err error) { + return coInitialize() +} + +// Uninitialize COM. +func (*Connection) Uninitialize() { + CoUninitialize() +} + +// Create IUnknown object based first on ProgId and then from String. +func (c *Connection) Create(progId string) (err error) { + var clsid *GUID + clsid, err = CLSIDFromProgID(progId) + if err != nil { + clsid, err = CLSIDFromString(progId) + if err != nil { + return + } + } + + unknown, err := CreateInstance(clsid, IID_IUnknown) + if err != nil { + return + } + c.Object = unknown + + return +} + +// Release IUnknown object. +func (c *Connection) Release() { + c.Object.Release() +} + +// Load COM object from list of programIDs or strings. +func (c *Connection) Load(names ...string) (errors []error) { + var tempErrors []error = make([]error, len(names)) + var numErrors int = 0 + for _, name := range names { + err := c.Create(name) + if err != nil { + tempErrors = append(tempErrors, err) + numErrors += 1 + continue + } + break + } + + copy(errors, tempErrors[0:numErrors]) + return +} + +// Dispatch returns Dispatch object. +func (c *Connection) Dispatch() (object *Dispatch, err error) { + dispatch, err := c.Object.QueryInterface(IID_IDispatch) + if err != nil { + return + } + object = &Dispatch{dispatch} + return +} + +// Dispatch stores IDispatch object. +type Dispatch struct { + Object *IDispatch // Dispatch object. +} + +// Call method on IDispatch with parameters. +func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(method) + if err != nil { + return + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + return +} + +// MustCall method on IDispatch with parameters. +func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(method) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_METHOD, params) + if err != nil { + panic(err) + } + + return +} + +// Get property on IDispatch with parameters. +func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + return +} + +// MustGet property on IDispatch with parameters. +func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params) + if err != nil { + panic(err) + } + return +} + +// Set property on IDispatch with parameters. +func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) { + id, err := d.GetId(name) + if err != nil { + return + } + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + return +} + +// MustSet property on IDispatch with parameters. +func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) { + id, err := d.GetId(name) + if err != nil { + panic(err) + } + + result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params) + if err != nil { + panic(err) + } + return +} + +// GetId retrieves ID of name on IDispatch. +func (d *Dispatch) GetId(name string) (id int32, err error) { + var dispid []int32 + dispid, err = d.Object.GetIDsOfName([]string{name}) + if err != nil { + return + } + id = dispid[0] + return +} + +// GetIds retrieves all IDs of names on IDispatch. +func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) { + dispid, err = d.Object.GetIDsOfName(names) + return +} + +// Invoke IDispatch on DisplayID of dispatch type with parameters. +// +// There have been problems where if send cascading params..., it would error +// out because the parameters would be empty. +func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) { + if len(params) < 1 { + result, err = d.Object.Invoke(id, dispatch) + } else { + result, err = d.Object.Invoke(id, dispatch, params...) + } + return +} + +// Release IDispatch object. +func (d *Dispatch) Release() { + d.Object.Release() +} + +// Connect initializes COM and attempts to load IUnknown based on given names. +func Connect(names ...string) (connection *Connection) { + connection.Initialize() + connection.Load(names...) + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/constants.go b/cli/vendor/github.com/go-ole/go-ole/constants.go new file mode 100644 index 0000000..fd0c6d7 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/constants.go @@ -0,0 +1,153 @@ +package ole + +const ( + CLSCTX_INPROC_SERVER = 1 + CLSCTX_INPROC_HANDLER = 2 + CLSCTX_LOCAL_SERVER = 4 + CLSCTX_INPROC_SERVER16 = 8 + CLSCTX_REMOTE_SERVER = 16 + CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER + CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER + CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER +) + +const ( + COINIT_APARTMENTTHREADED = 0x2 + COINIT_MULTITHREADED = 0x0 + COINIT_DISABLE_OLE1DDE = 0x4 + COINIT_SPEED_OVER_MEMORY = 0x8 +) + +const ( + DISPATCH_METHOD = 1 + DISPATCH_PROPERTYGET = 2 + DISPATCH_PROPERTYPUT = 4 + DISPATCH_PROPERTYPUTREF = 8 +) + +const ( + S_OK = 0x00000000 + E_UNEXPECTED = 0x8000FFFF + E_NOTIMPL = 0x80004001 + E_OUTOFMEMORY = 0x8007000E + E_INVALIDARG = 0x80070057 + E_NOINTERFACE = 0x80004002 + E_POINTER = 0x80004003 + E_HANDLE = 0x80070006 + E_ABORT = 0x80004004 + E_FAIL = 0x80004005 + E_ACCESSDENIED = 0x80070005 + E_PENDING = 0x8000000A + + CO_E_CLASSSTRING = 0x800401F3 +) + +const ( + CC_FASTCALL = iota + CC_CDECL + CC_MSCPASCAL + CC_PASCAL = CC_MSCPASCAL + CC_MACPASCAL + CC_STDCALL + CC_FPFASTCALL + CC_SYSCALL + CC_MPWCDECL + CC_MPWPASCAL + CC_MAX = CC_MPWPASCAL +) + +type VT uint16 + +const ( + VT_EMPTY VT = 0x0 + VT_NULL VT = 0x1 + VT_I2 VT = 0x2 + VT_I4 VT = 0x3 + VT_R4 VT = 0x4 + VT_R8 VT = 0x5 + VT_CY VT = 0x6 + VT_DATE VT = 0x7 + VT_BSTR VT = 0x8 + VT_DISPATCH VT = 0x9 + VT_ERROR VT = 0xa + VT_BOOL VT = 0xb + VT_VARIANT VT = 0xc + VT_UNKNOWN VT = 0xd + VT_DECIMAL VT = 0xe + VT_I1 VT = 0x10 + VT_UI1 VT = 0x11 + VT_UI2 VT = 0x12 + VT_UI4 VT = 0x13 + VT_I8 VT = 0x14 + VT_UI8 VT = 0x15 + VT_INT VT = 0x16 + VT_UINT VT = 0x17 + VT_VOID VT = 0x18 + VT_HRESULT VT = 0x19 + VT_PTR VT = 0x1a + VT_SAFEARRAY VT = 0x1b + VT_CARRAY VT = 0x1c + VT_USERDEFINED VT = 0x1d + VT_LPSTR VT = 0x1e + VT_LPWSTR VT = 0x1f + VT_RECORD VT = 0x24 + VT_INT_PTR VT = 0x25 + VT_UINT_PTR VT = 0x26 + VT_FILETIME VT = 0x40 + VT_BLOB VT = 0x41 + VT_STREAM VT = 0x42 + VT_STORAGE VT = 0x43 + VT_STREAMED_OBJECT VT = 0x44 + VT_STORED_OBJECT VT = 0x45 + VT_BLOB_OBJECT VT = 0x46 + VT_CF VT = 0x47 + VT_CLSID VT = 0x48 + VT_BSTR_BLOB VT = 0xfff + VT_VECTOR VT = 0x1000 + VT_ARRAY VT = 0x2000 + VT_BYREF VT = 0x4000 + VT_RESERVED VT = 0x8000 + VT_ILLEGAL VT = 0xffff + VT_ILLEGALMASKED VT = 0xfff + VT_TYPEMASK VT = 0xfff +) + +const ( + DISPID_UNKNOWN = -1 + DISPID_VALUE = 0 + DISPID_PROPERTYPUT = -3 + DISPID_NEWENUM = -4 + DISPID_EVALUATE = -5 + DISPID_CONSTRUCTOR = -6 + DISPID_DESTRUCTOR = -7 + DISPID_COLLECT = -8 +) + +const ( + TKIND_ENUM = 1 + TKIND_RECORD = 2 + TKIND_MODULE = 3 + TKIND_INTERFACE = 4 + TKIND_DISPATCH = 5 + TKIND_COCLASS = 6 + TKIND_ALIAS = 7 + TKIND_UNION = 8 + TKIND_MAX = 9 +) + +// Safe Array Feature Flags + +const ( + FADF_AUTO = 0x0001 + FADF_STATIC = 0x0002 + FADF_EMBEDDED = 0x0004 + FADF_FIXEDSIZE = 0x0010 + FADF_RECORD = 0x0020 + FADF_HAVEIID = 0x0040 + FADF_HAVEVARTYPE = 0x0080 + FADF_BSTR = 0x0100 + FADF_UNKNOWN = 0x0200 + FADF_DISPATCH = 0x0400 + FADF_VARIANT = 0x0800 + FADF_RESERVED = 0xF008 +) diff --git a/cli/vendor/github.com/go-ole/go-ole/error.go b/cli/vendor/github.com/go-ole/go-ole/error.go new file mode 100644 index 0000000..096b456 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/error.go @@ -0,0 +1,51 @@ +package ole + +// OleError stores COM errors. +type OleError struct { + hr uintptr + description string + subError error +} + +// NewError creates new error with HResult. +func NewError(hr uintptr) *OleError { + return &OleError{hr: hr} +} + +// NewErrorWithDescription creates new COM error with HResult and description. +func NewErrorWithDescription(hr uintptr, description string) *OleError { + return &OleError{hr: hr, description: description} +} + +// NewErrorWithSubError creates new COM error with parent error. +func NewErrorWithSubError(hr uintptr, description string, err error) *OleError { + return &OleError{hr: hr, description: description, subError: err} +} + +// Code is the HResult. +func (v *OleError) Code() uintptr { + return uintptr(v.hr) +} + +// String description, either manually set or format message with error code. +func (v *OleError) String() string { + if v.description != "" { + return errstr(int(v.hr)) + " (" + v.description + ")" + } + return errstr(int(v.hr)) +} + +// Error implements error interface. +func (v *OleError) Error() string { + return v.String() +} + +// Description retrieves error summary, if there is one. +func (v *OleError) Description() string { + return v.description +} + +// SubError returns parent error, if there is one. +func (v *OleError) SubError() error { + return v.subError +} diff --git a/cli/vendor/github.com/go-ole/go-ole/error_func.go b/cli/vendor/github.com/go-ole/go-ole/error_func.go new file mode 100644 index 0000000..8a2ffaa --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/error_func.go @@ -0,0 +1,8 @@ +// +build !windows + +package ole + +// errstr converts error code to string. +func errstr(errno int) string { + return "" +} diff --git a/cli/vendor/github.com/go-ole/go-ole/error_windows.go b/cli/vendor/github.com/go-ole/go-ole/error_windows.go new file mode 100644 index 0000000..d0e8e68 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/error_windows.go @@ -0,0 +1,24 @@ +// +build windows + +package ole + +import ( + "fmt" + "syscall" + "unicode/utf16" +) + +// errstr converts error code to string. +func errstr(errno int) string { + // ask windows for the remaining errors + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS + b := make([]uint16, 300) + n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil) + if err != nil { + return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err) + } + // trim terminating \r and \n + for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { + } + return string(utf16.Decode(b[:n])) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/guid.go b/cli/vendor/github.com/go-ole/go-ole/guid.go new file mode 100644 index 0000000..8d20f68 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/guid.go @@ -0,0 +1,284 @@ +package ole + +var ( + // IID_NULL is null Interface ID, used when no other Interface ID is known. + IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}") + + // IID_IUnknown is for IUnknown interfaces. + IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}") + + // IID_IDispatch is for IDispatch interfaces. + IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}") + + // IID_IEnumVariant is for IEnumVariant interfaces + IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}") + + // IID_IConnectionPointContainer is for IConnectionPointContainer interfaces. + IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}") + + // IID_IConnectionPoint is for IConnectionPoint interfaces. + IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}") + + // IID_IInspectable is for IInspectable interfaces. + IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}") + + // IID_IProvideClassInfo is for IProvideClassInfo interfaces. + IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}") +) + +// These are for testing and not part of any library. +var ( + // IID_ICOMTestString is for ICOMTestString interfaces. + // + // {E0133EB4-C36F-469A-9D3D-C66B84BE19ED} + IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}") + + // IID_ICOMTestInt8 is for ICOMTestInt8 interfaces. + // + // {BEB06610-EB84-4155-AF58-E2BFF53680B4} + IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}") + + // IID_ICOMTestInt16 is for ICOMTestInt16 interfaces. + // + // {DAA3F9FA-761E-4976-A860-8364CE55F6FC} + IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}") + + // IID_ICOMTestInt32 is for ICOMTestInt32 interfaces. + // + // {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0} + IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}") + + // IID_ICOMTestInt64 is for ICOMTestInt64 interfaces. + // + // {8D437CBC-B3ED-485C-BC32-C336432A1623} + IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}") + + // IID_ICOMTestFloat is for ICOMTestFloat interfaces. + // + // {BF1ED004-EA02-456A-AA55-2AC8AC6B054C} + IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}") + + // IID_ICOMTestDouble is for ICOMTestDouble interfaces. + // + // {BF908A81-8687-4E93-999F-D86FAB284BA0} + IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}") + + // IID_ICOMTestBoolean is for ICOMTestBoolean interfaces. + // + // {D530E7A6-4EE8-40D1-8931-3D63B8605010} + IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}") + + // IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces. + // + // {6485B1EF-D780-4834-A4FE-1EBB51746CA3} + IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}") + + // IID_ICOMTestTypes is for ICOMTestTypes interfaces. + // + // {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0} + IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}") + + // CLSID_COMEchoTestObject is for COMEchoTestObject class. + // + // {3C24506A-AE9E-4D50-9157-EF317281F1B0} + CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}") + + // CLSID_COMTestScalarClass is for COMTestScalarClass class. + // + // {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86} + CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}") +) + +const hextable = "0123456789ABCDEF" +const emptyGUID = "{00000000-0000-0000-0000-000000000000}" + +// GUID is Windows API specific GUID type. +// +// This exists to match Windows GUID type for direct passing for COM. +// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// NewGUID converts the given string into a globally unique identifier that is +// compliant with the Windows API. +// +// The supplied string may be in any of these formats: +// +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// The conversion of the supplied string is not case-sensitive. +func NewGUID(guid string) *GUID { + d := []byte(guid) + var d1, d2, d3, d4a, d4b []byte + + switch len(d) { + case 38: + if d[0] != '{' || d[37] != '}' { + return nil + } + d = d[1:37] + fallthrough + case 36: + if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' { + return nil + } + d1 = d[0:8] + d2 = d[9:13] + d3 = d[14:18] + d4a = d[19:23] + d4b = d[24:36] + case 32: + d1 = d[0:8] + d2 = d[8:12] + d3 = d[12:16] + d4a = d[16:20] + d4b = d[20:32] + default: + return nil + } + + var g GUID + var ok1, ok2, ok3, ok4 bool + g.Data1, ok1 = decodeHexUint32(d1) + g.Data2, ok2 = decodeHexUint16(d2) + g.Data3, ok3 = decodeHexUint16(d3) + g.Data4, ok4 = decodeHexByte64(d4a, d4b) + if ok1 && ok2 && ok3 && ok4 { + return &g + } + return nil +} + +func decodeHexUint32(src []byte) (value uint32, ok bool) { + var b1, b2, b3, b4 byte + var ok1, ok2, ok3, ok4 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + b3, ok3 = decodeHexByte(src[4], src[5]) + b4, ok4 = decodeHexByte(src[6], src[7]) + value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4) + ok = ok1 && ok2 && ok3 && ok4 + return +} + +func decodeHexUint16(src []byte) (value uint16, ok bool) { + var b1, b2 byte + var ok1, ok2 bool + b1, ok1 = decodeHexByte(src[0], src[1]) + b2, ok2 = decodeHexByte(src[2], src[3]) + value = (uint16(b1) << 8) | uint16(b2) + ok = ok1 && ok2 + return +} + +func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) { + var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool + value[0], ok1 = decodeHexByte(s1[0], s1[1]) + value[1], ok2 = decodeHexByte(s1[2], s1[3]) + value[2], ok3 = decodeHexByte(s2[0], s2[1]) + value[3], ok4 = decodeHexByte(s2[2], s2[3]) + value[4], ok5 = decodeHexByte(s2[4], s2[5]) + value[5], ok6 = decodeHexByte(s2[6], s2[7]) + value[6], ok7 = decodeHexByte(s2[8], s2[9]) + value[7], ok8 = decodeHexByte(s2[10], s2[11]) + ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8 + return +} + +func decodeHexByte(c1, c2 byte) (value byte, ok bool) { + var n1, n2 byte + var ok1, ok2 bool + n1, ok1 = decodeHexChar(c1) + n2, ok2 = decodeHexChar(c2) + value = (n1 << 4) | n2 + ok = ok1 && ok2 + return +} + +func decodeHexChar(c byte) (byte, bool) { + switch { + case '0' <= c && c <= '9': + return c - '0', true + case 'a' <= c && c <= 'f': + return c - 'a' + 10, true + case 'A' <= c && c <= 'F': + return c - 'A' + 10, true + } + + return 0, false +} + +// String converts the GUID to string form. It will adhere to this pattern: +// +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// +// If the GUID is nil, the string representation of an empty GUID is returned: +// +// {00000000-0000-0000-0000-000000000000} +func (guid *GUID) String() string { + if guid == nil { + return emptyGUID + } + + var c [38]byte + c[0] = '{' + putUint32Hex(c[1:9], guid.Data1) + c[9] = '-' + putUint16Hex(c[10:14], guid.Data2) + c[14] = '-' + putUint16Hex(c[15:19], guid.Data3) + c[19] = '-' + putByteHex(c[20:24], guid.Data4[0:2]) + c[24] = '-' + putByteHex(c[25:37], guid.Data4[2:8]) + c[37] = '}' + return string(c[:]) +} + +func putUint32Hex(b []byte, v uint32) { + b[0] = hextable[byte(v>>24)>>4] + b[1] = hextable[byte(v>>24)&0x0f] + b[2] = hextable[byte(v>>16)>>4] + b[3] = hextable[byte(v>>16)&0x0f] + b[4] = hextable[byte(v>>8)>>4] + b[5] = hextable[byte(v>>8)&0x0f] + b[6] = hextable[byte(v)>>4] + b[7] = hextable[byte(v)&0x0f] +} + +func putUint16Hex(b []byte, v uint16) { + b[0] = hextable[byte(v>>8)>>4] + b[1] = hextable[byte(v>>8)&0x0f] + b[2] = hextable[byte(v)>>4] + b[3] = hextable[byte(v)&0x0f] +} + +func putByteHex(dst, src []byte) { + for i := 0; i < len(src); i++ { + dst[i*2] = hextable[src[i]>>4] + dst[i*2+1] = hextable[src[i]&0x0f] + } +} + +// IsEqualGUID compares two GUID. +// +// Not constant time comparison. +func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { + return guid1.Data1 == guid2.Data1 && + guid1.Data2 == guid2.Data2 && + guid1.Data3 == guid2.Data3 && + guid1.Data4[0] == guid2.Data4[0] && + guid1.Data4[1] == guid2.Data4[1] && + guid1.Data4[2] == guid2.Data4[2] && + guid1.Data4[3] == guid2.Data4[3] && + guid1.Data4[4] == guid2.Data4[4] && + guid1.Data4[5] == guid2.Data4[5] && + guid1.Data4[6] == guid2.Data4[6] && + guid1.Data4[7] == guid2.Data4[7] +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint.go new file mode 100644 index 0000000..9e6c49f --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint.go @@ -0,0 +1,20 @@ +package ole + +import "unsafe" + +type IConnectionPoint struct { + IUnknown +} + +type IConnectionPointVtbl struct { + IUnknownVtbl + GetConnectionInterface uintptr + GetConnectionPointContainer uintptr + Advise uintptr + Unadvise uintptr + EnumConnections uintptr +} + +func (v *IConnectionPoint) VTable() *IConnectionPointVtbl { + return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go new file mode 100644 index 0000000..5414dc3 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_func.go @@ -0,0 +1,21 @@ +// +build !windows + +package ole + +import "unsafe" + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + return int32(0) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) { + return NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go new file mode 100644 index 0000000..32bc183 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpoint_windows.go @@ -0,0 +1,43 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 { + // XXX: This doesn't look like it does what it's supposed to + return release((*IUnknown)(unsafe.Pointer(v))) +} + +func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Advise, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(unknown)), + uintptr(unsafe.Pointer(&cookie))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().Unadvise, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(cookie), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go new file mode 100644 index 0000000..165860d --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer.go @@ -0,0 +1,17 @@ +package ole + +import "unsafe" + +type IConnectionPointContainer struct { + IUnknown +} + +type IConnectionPointContainerVtbl struct { + IUnknownVtbl + EnumConnectionPoints uintptr + FindConnectionPoint uintptr +} + +func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl { + return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go new file mode 100644 index 0000000..5dfa42a --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_func.go @@ -0,0 +1,11 @@ +// +build !windows + +package ole + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error { + return NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go new file mode 100644 index 0000000..ad30d79 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iconnectionpointcontainer_windows.go @@ -0,0 +1,25 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error { + return NewError(E_NOTIMPL) +} + +func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) { + hr, _, _ := syscall.Syscall( + v.VTable().FindConnectionPoint, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(point))) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/idispatch.go b/cli/vendor/github.com/go-ole/go-ole/idispatch.go new file mode 100644 index 0000000..d4af124 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/idispatch.go @@ -0,0 +1,94 @@ +package ole + +import "unsafe" + +type IDispatch struct { + IUnknown +} + +type IDispatchVtbl struct { + IUnknownVtbl + GetTypeInfoCount uintptr + GetTypeInfo uintptr + GetIDsOfNames uintptr + Invoke uintptr +} + +func (v *IDispatch) VTable() *IDispatchVtbl { + return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) { + dispid, err = getIDsOfName(v, names) + return +} + +func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + result, err = invoke(v, dispid, dispatch, params...) + return +} + +func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) { + c, err = getTypeInfoCount(v) + return +} + +func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) { + tinfo, err = getTypeInfo(v) + return +} + +// GetSingleIDOfName is a helper that returns single display ID for IDispatch name. +// +// This replaces the common pattern of attempting to get a single name from the list of available +// IDs. It gives the first ID, if it is available. +func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) { + var displayIDs []int32 + displayIDs, err = v.GetIDsOfName([]string{name}) + if err != nil { + return + } + displayID = displayIDs[0] + return +} + +// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke. +// +// Accepts name and will attempt to retrieve Display ID to pass to Invoke. +// +// Passing params as an array is a workaround that could be fixed in later versions of Go that +// prevent passing empty params. During testing it was discovered that this is an acceptable way of +// getting around not being able to pass params normally. +func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) { + displayID, err := v.GetSingleIDOfName(name) + if err != nil { + return + } + + if len(params) < 1 { + result, err = v.Invoke(displayID, dispatch) + } else { + result, err = v.Invoke(displayID, dispatch, params...) + } + + return +} + +// CallMethod invokes named function with arguments on object. +func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params) +} + +// GetProperty retrieves the property with the name with the ability to pass arguments. +// +// Most of the time you will not need to pass arguments as most objects do not allow for this +// feature. Or at least, should not allow for this feature. Some servers don't follow best practices +// and this is provided for those edge cases. +func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params) +} + +// PutProperty attempts to mutate a property in the object. +func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) { + return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/idispatch_func.go b/cli/vendor/github.com/go-ole/go-ole/idispatch_func.go new file mode 100644 index 0000000..b8fbbe3 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/idispatch_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) { + return []int32{}, NewError(E_NOTIMPL) +} + +func getTypeInfoCount(disp *IDispatch) (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} + +func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) { + return nil, NewError(E_NOTIMPL) +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/idispatch_windows.go b/cli/vendor/github.com/go-ole/go-ole/idispatch_windows.go new file mode 100644 index 0000000..6ec180b --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/idispatch_windows.go @@ -0,0 +1,200 @@ +// +build windows + +package ole + +import ( + "math/big" + "syscall" + "time" + "unsafe" +) + +func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) { + wnames := make([]*uint16, len(names)) + for i := 0; i < len(names); i++ { + wnames[i] = syscall.StringToUTF16Ptr(names[i]) + } + dispid = make([]int32, len(names)) + namelen := uint32(len(names)) + hr, _, _ := syscall.Syscall6( + disp.VTable().GetIDsOfNames, + 6, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(unsafe.Pointer(&wnames[0])), + uintptr(namelen), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&dispid[0]))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfoCount(disp *IDispatch) (c uint32, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfoCount, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&c)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetTypeInfo, + 3, + uintptr(unsafe.Pointer(disp)), + uintptr(GetUserDefaultLCID()), + uintptr(unsafe.Pointer(&tinfo))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) { + var dispparams DISPPARAMS + + if dispatch&DISPATCH_PROPERTYPUT != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } else if dispatch&DISPATCH_PROPERTYPUTREF != 0 { + dispnames := [1]int32{DISPID_PROPERTYPUT} + dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0])) + dispparams.cNamedArgs = 1 + } + var vargs []VARIANT + if len(params) > 0 { + vargs = make([]VARIANT, len(params)) + for i, v := range params { + //n := len(params)-i-1 + n := len(params) - i - 1 + VariantInit(&vargs[n]) + switch vv := v.(type) { + case bool: + if vv { + vargs[n] = NewVariant(VT_BOOL, 0xffff) + } else { + vargs[n] = NewVariant(VT_BOOL, 0) + } + case *bool: + vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool))))) + case uint8: + vargs[n] = NewVariant(VT_I1, int64(v.(uint8))) + case *uint8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int8: + vargs[n] = NewVariant(VT_I1, int64(v.(int8))) + case *int8: + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + case int16: + vargs[n] = NewVariant(VT_I2, int64(v.(int16))) + case *int16: + vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16))))) + case uint16: + vargs[n] = NewVariant(VT_UI2, int64(v.(uint16))) + case *uint16: + vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16))))) + case int32: + vargs[n] = NewVariant(VT_I4, int64(v.(int32))) + case *int32: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32))))) + case uint32: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint32))) + case *uint32: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32))))) + case int64: + vargs[n] = NewVariant(VT_I8, int64(v.(int64))) + case *int64: + vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64))))) + case uint64: + vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64)))) + case *uint64: + vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64))))) + case int: + vargs[n] = NewVariant(VT_I4, int64(v.(int))) + case *int: + vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int))))) + case uint: + vargs[n] = NewVariant(VT_UI4, int64(v.(uint))) + case *uint: + vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint))))) + case float32: + vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv))) + case *float32: + vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32))))) + case float64: + vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv))) + case *float64: + vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64))))) + case *big.Int: + vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64()) + case string: + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string)))))) + case *string: + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string))))) + case time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s))))) + case *time.Time: + s := vv.Format("2006-01-02 15:04:05") + vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s)))) + case *IDispatch: + vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))) + case **IDispatch: + vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))) + case nil: + vargs[n] = NewVariant(VT_NULL, 0) + case *VARIANT: + vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))) + case []byte: + safeByteArray := safeArrayFromByteSlice(v.([]byte)) + vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + case []string: + safeByteArray := safeArrayFromStringSlice(v.([]string)) + vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray)))) + defer VariantClear(&vargs[n]) + default: + panic("unknown type") + } + } + dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0])) + dispparams.cArgs = uint32(len(params)) + } + + result = new(VARIANT) + var excepInfo EXCEPINFO + VariantInit(result) + hr, _, _ := syscall.Syscall9( + disp.VTable().Invoke, + 9, + uintptr(unsafe.Pointer(disp)), + uintptr(dispid), + uintptr(unsafe.Pointer(IID_NULL)), + uintptr(GetUserDefaultLCID()), + uintptr(dispatch), + uintptr(unsafe.Pointer(&dispparams)), + uintptr(unsafe.Pointer(result)), + uintptr(unsafe.Pointer(&excepInfo)), + 0) + if hr != 0 { + err = NewErrorWithSubError(hr, BstrToString(excepInfo.bstrDescription), excepInfo) + } + for i, varg := range vargs { + n := len(params) - i - 1 + if varg.VT == VT_BSTR && varg.Val != 0 { + SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val))))) + } + if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 { + *(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val)))) + } + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/ienumvariant.go b/cli/vendor/github.com/go-ole/go-ole/ienumvariant.go new file mode 100644 index 0000000..2433897 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/ienumvariant.go @@ -0,0 +1,19 @@ +package ole + +import "unsafe" + +type IEnumVARIANT struct { + IUnknown +} + +type IEnumVARIANTVtbl struct { + IUnknownVtbl + Next uintptr + Skip uintptr + Reset uintptr + Clone uintptr +} + +func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl { + return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/ienumvariant_func.go b/cli/vendor/github.com/go-ole/go-ole/ienumvariant_func.go new file mode 100644 index 0000000..c148481 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/ienumvariant_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) { + return nil, NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Reset() error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Skip(celt uint) error { + return NewError(E_NOTIMPL) +} + +func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) { + return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go b/cli/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go new file mode 100644 index 0000000..4781f3b --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/ienumvariant_windows.go @@ -0,0 +1,63 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Clone, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(unsafe.Pointer(&cloned)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Reset() (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Reset, + 1, + uintptr(unsafe.Pointer(enum)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Skip(celt uint) (err error) { + hr, _, _ := syscall.Syscall( + enum.VTable().Skip, + 2, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} + +func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) { + hr, _, _ := syscall.Syscall6( + enum.VTable().Next, + 4, + uintptr(unsafe.Pointer(enum)), + uintptr(celt), + uintptr(unsafe.Pointer(&array)), + uintptr(unsafe.Pointer(&length)), + 0, + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iinspectable.go b/cli/vendor/github.com/go-ole/go-ole/iinspectable.go new file mode 100644 index 0000000..f4a19e2 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iinspectable.go @@ -0,0 +1,18 @@ +package ole + +import "unsafe" + +type IInspectable struct { + IUnknown +} + +type IInspectableVtbl struct { + IUnknownVtbl + GetIIds uintptr + GetRuntimeClassName uintptr + GetTrustLevel uintptr +} + +func (v *IInspectable) VTable() *IInspectableVtbl { + return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iinspectable_func.go b/cli/vendor/github.com/go-ole/go-ole/iinspectable_func.go new file mode 100644 index 0000000..348829b --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iinspectable_func.go @@ -0,0 +1,15 @@ +// +build !windows + +package ole + +func (v *IInspectable) GetIids() ([]*GUID, error) { + return []*GUID{}, NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetRuntimeClassName() (string, error) { + return "", NewError(E_NOTIMPL) +} + +func (v *IInspectable) GetTrustLevel() (uint32, error) { + return uint32(0), NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iinspectable_windows.go b/cli/vendor/github.com/go-ole/go-ole/iinspectable_windows.go new file mode 100644 index 0000000..4519a4a --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iinspectable_windows.go @@ -0,0 +1,72 @@ +// +build windows + +package ole + +import ( + "bytes" + "encoding/binary" + "reflect" + "syscall" + "unsafe" +) + +func (v *IInspectable) GetIids() (iids []*GUID, err error) { + var count uint32 + var array uintptr + hr, _, _ := syscall.Syscall( + v.VTable().GetIIds, + 3, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&count)), + uintptr(unsafe.Pointer(&array))) + if hr != 0 { + err = NewError(hr) + return + } + defer CoTaskMemFree(array) + + iids = make([]*GUID, count) + byteCount := count * uint32(unsafe.Sizeof(GUID{})) + slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)} + byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr)) + reader := bytes.NewReader(byteSlice) + for i := range iids { + guid := GUID{} + err = binary.Read(reader, binary.LittleEndian, &guid) + if err != nil { + return + } + iids[i] = &guid + } + return +} + +func (v *IInspectable) GetRuntimeClassName() (s string, err error) { + var hstring HString + hr, _, _ := syscall.Syscall( + v.VTable().GetRuntimeClassName, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&hstring)), + 0) + if hr != 0 { + err = NewError(hr) + return + } + s = hstring.String() + DeleteHString(hstring) + return +} + +func (v *IInspectable) GetTrustLevel() (level uint32, err error) { + hr, _, _ := syscall.Syscall( + v.VTable().GetTrustLevel, + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&level)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go new file mode 100644 index 0000000..25f3a6f --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo.go @@ -0,0 +1,21 @@ +package ole + +import "unsafe" + +type IProvideClassInfo struct { + IUnknown +} + +type IProvideClassInfoVtbl struct { + IUnknownVtbl + GetClassInfo uintptr +} + +func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl { + return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) { + cinfo, err = getClassInfo(v) + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go new file mode 100644 index 0000000..7e3cb63 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go new file mode 100644 index 0000000..2ad0163 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iprovideclassinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) { + hr, _, _ := syscall.Syscall( + disp.VTable().GetClassInfo, + 2, + uintptr(unsafe.Pointer(disp)), + uintptr(unsafe.Pointer(&tinfo)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/itypeinfo.go b/cli/vendor/github.com/go-ole/go-ole/itypeinfo.go new file mode 100644 index 0000000..dd3c5e2 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/itypeinfo.go @@ -0,0 +1,34 @@ +package ole + +import "unsafe" + +type ITypeInfo struct { + IUnknown +} + +type ITypeInfoVtbl struct { + IUnknownVtbl + GetTypeAttr uintptr + GetTypeComp uintptr + GetFuncDesc uintptr + GetVarDesc uintptr + GetNames uintptr + GetRefTypeOfImplType uintptr + GetImplTypeFlags uintptr + GetIDsOfNames uintptr + Invoke uintptr + GetDocumentation uintptr + GetDllEntry uintptr + GetRefTypeInfo uintptr + AddressOfMember uintptr + CreateInstance uintptr + GetMops uintptr + GetContainingTypeLib uintptr + ReleaseTypeAttr uintptr + ReleaseFuncDesc uintptr + ReleaseVarDesc uintptr +} + +func (v *ITypeInfo) VTable() *ITypeInfoVtbl { + return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable)) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/itypeinfo_func.go b/cli/vendor/github.com/go-ole/go-ole/itypeinfo_func.go new file mode 100644 index 0000000..8364a65 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/itypeinfo_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) { + return nil, NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go b/cli/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go new file mode 100644 index 0000000..54782b3 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/itypeinfo_windows.go @@ -0,0 +1,21 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) { + hr, _, _ := syscall.Syscall( + uintptr(v.VTable().GetTypeAttr), + 2, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(&tattr)), + 0) + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iunknown.go b/cli/vendor/github.com/go-ole/go-ole/iunknown.go new file mode 100644 index 0000000..108f28e --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iunknown.go @@ -0,0 +1,57 @@ +package ole + +import "unsafe" + +type IUnknown struct { + RawVTable *interface{} +} + +type IUnknownVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr +} + +type UnknownLike interface { + QueryInterface(iid *GUID) (disp *IDispatch, err error) + AddRef() int32 + Release() int32 +} + +func (v *IUnknown) VTable() *IUnknownVtbl { + return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable)) +} + +func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error { + return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj) +} + +func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) { + err = v.PutQueryInterface(interfaceID, &dispatch) + return +} + +func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) { + err = v.PutQueryInterface(interfaceID, &enum) + return +} + +func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) { + return queryInterface(v, iid) +} + +func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) { + unk, err := queryInterface(v, iid) + if err != nil { + panic(err) + } + return unk +} + +func (v *IUnknown) AddRef() int32 { + return addRef(v) +} + +func (v *IUnknown) Release() int32 { + return release(v) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iunknown_func.go b/cli/vendor/github.com/go-ole/go-ole/iunknown_func.go new file mode 100644 index 0000000..d0a62cf --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iunknown_func.go @@ -0,0 +1,19 @@ +// +build !windows + +package ole + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + return NewError(E_NOTIMPL) +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + return nil, NewError(E_NOTIMPL) +} + +func addRef(unk *IUnknown) int32 { + return 0 +} + +func release(unk *IUnknown) int32 { + return 0 +} diff --git a/cli/vendor/github.com/go-ole/go-ole/iunknown_windows.go b/cli/vendor/github.com/go-ole/go-ole/iunknown_windows.go new file mode 100644 index 0000000..ede5bb8 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/iunknown_windows.go @@ -0,0 +1,58 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unsafe" +) + +func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) { + selfValue := reflect.ValueOf(self).Elem() + objValue := reflect.ValueOf(obj).Elem() + + hr, _, _ := syscall.Syscall( + method, + 3, + selfValue.UnsafeAddr(), + uintptr(unsafe.Pointer(interfaceID)), + objValue.Addr().Pointer()) + if hr != 0 { + err = NewError(hr) + } + return +} + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + hr, _, _ := syscall.Syscall( + unk.VTable().QueryInterface, + 3, + uintptr(unsafe.Pointer(unk)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func addRef(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().AddRef, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} + +func release(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().Release, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/ole.go b/cli/vendor/github.com/go-ole/go-ole/ole.go new file mode 100644 index 0000000..e2ae4f4 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/ole.go @@ -0,0 +1,157 @@ +package ole + +import ( + "fmt" + "strings" +) + +// DISPPARAMS are the arguments that passed to methods or property. +type DISPPARAMS struct { + rgvarg uintptr + rgdispidNamedArgs uintptr + cArgs uint32 + cNamedArgs uint32 +} + +// EXCEPINFO defines exception info. +type EXCEPINFO struct { + wCode uint16 + wReserved uint16 + bstrSource *uint16 + bstrDescription *uint16 + bstrHelpFile *uint16 + dwHelpContext uint32 + pvReserved uintptr + pfnDeferredFillIn uintptr + scode uint32 +} + +// WCode return wCode in EXCEPINFO. +func (e EXCEPINFO) WCode() uint16 { + return e.wCode +} + +// SCODE return scode in EXCEPINFO. +func (e EXCEPINFO) SCODE() uint32 { + return e.scode +} + +// String convert EXCEPINFO to string. +func (e EXCEPINFO) String() string { + var src, desc, hlp string + if e.bstrSource == nil { + src = "" + } else { + src = BstrToString(e.bstrSource) + } + + if e.bstrDescription == nil { + desc = "" + } else { + desc = BstrToString(e.bstrDescription) + } + + if e.bstrHelpFile == nil { + hlp = "" + } else { + hlp = BstrToString(e.bstrHelpFile) + } + + return fmt.Sprintf( + "wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x", + e.wCode, src, desc, hlp, e.dwHelpContext, e.scode, + ) +} + +// Error implements error interface and returns error string. +func (e EXCEPINFO) Error() string { + if e.bstrDescription != nil { + return strings.TrimSpace(BstrToString(e.bstrDescription)) + } + + src := "Unknown" + if e.bstrSource != nil { + src = BstrToString(e.bstrSource) + } + + code := e.scode + if e.wCode != 0 { + code = uint32(e.wCode) + } + + return fmt.Sprintf("%v: %#x", src, code) +} + +// PARAMDATA defines parameter data type. +type PARAMDATA struct { + Name *int16 + Vt uint16 +} + +// METHODDATA defines method info. +type METHODDATA struct { + Name *uint16 + Data *PARAMDATA + Dispid int32 + Meth uint32 + CC int32 + CArgs uint32 + Flags uint16 + VtReturn uint32 +} + +// INTERFACEDATA defines interface info. +type INTERFACEDATA struct { + MethodData *METHODDATA + CMembers uint32 +} + +// Point is 2D vector type. +type Point struct { + X int32 + Y int32 +} + +// Msg is message between processes. +type Msg struct { + Hwnd uint32 + Message uint32 + Wparam int32 + Lparam int32 + Time uint32 + Pt Point +} + +// TYPEDESC defines data type. +type TYPEDESC struct { + Hreftype uint32 + VT uint16 +} + +// IDLDESC defines IDL info. +type IDLDESC struct { + DwReserved uint32 + WIDLFlags uint16 +} + +// TYPEATTR defines type info. +type TYPEATTR struct { + Guid GUID + Lcid uint32 + dwReserved uint32 + MemidConstructor int32 + MemidDestructor int32 + LpstrSchema *uint16 + CbSizeInstance uint32 + Typekind int32 + CFuncs uint16 + CVars uint16 + CImplTypes uint16 + CbSizeVft uint16 + CbAlignment uint16 + WTypeFlags uint16 + WMajorVerNum uint16 + WMinorVerNum uint16 + TdescAlias TYPEDESC + IdldescType IDLDESC +} diff --git a/cli/vendor/github.com/go-ole/go-ole/safearray.go b/cli/vendor/github.com/go-ole/go-ole/safearray.go new file mode 100644 index 0000000..a5201b5 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/safearray.go @@ -0,0 +1,27 @@ +// Package is meant to retrieve and process safe array data returned from COM. + +package ole + +// SafeArrayBound defines the SafeArray boundaries. +type SafeArrayBound struct { + Elements uint32 + LowerBound int32 +} + +// SafeArray is how COM handles arrays. +type SafeArray struct { + Dimensions uint16 + FeaturesFlag uint16 + ElementsSize uint32 + LocksAmount uint32 + Data uint32 + Bounds [16]byte +} + +// SAFEARRAY is obsolete, exists for backwards compatibility. +// Use SafeArray +type SAFEARRAY SafeArray + +// SAFEARRAYBOUND is obsolete, exists for backwards compatibility. +// Use SafeArrayBound +type SAFEARRAYBOUND SafeArrayBound diff --git a/cli/vendor/github.com/go-ole/go-ole/safearray_func.go b/cli/vendor/github.com/go-ole/go-ole/safearray_func.go new file mode 100644 index 0000000..0dee670 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/safearray_func.go @@ -0,0 +1,211 @@ +// +build !windows + +package ole + +import ( + "unsafe" +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +func safeArrayAccessData(safearray *SafeArray) (uintptr, error) { + return uintptr(0), NewError(E_NOTIMPL) +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) { + u := uint32(0) + return &u, NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetElement retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) { + return "", NewError(E_NOTIMPL) +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (*GUID, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) { + return int32(0), NewError(E_NOTIMPL) +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (uint16, error) { + return uint16(0), NewError(E_NOTIMPL) +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) error { + return NewError(E_NOTIMPL) +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error { + return NewError(E_NOTIMPL) +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) { + return nil, NewError(E_NOTIMPL) +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error { + return NewError(E_NOTIMPL) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/safearray_windows.go b/cli/vendor/github.com/go-ole/go-ole/safearray_windows.go new file mode 100644 index 0000000..b48a239 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/safearray_windows.go @@ -0,0 +1,337 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +var ( + procSafeArrayAccessData, _ = modoleaut32.FindProc("SafeArrayAccessData") + procSafeArrayAllocData, _ = modoleaut32.FindProc("SafeArrayAllocData") + procSafeArrayAllocDescriptor, _ = modoleaut32.FindProc("SafeArrayAllocDescriptor") + procSafeArrayAllocDescriptorEx, _ = modoleaut32.FindProc("SafeArrayAllocDescriptorEx") + procSafeArrayCopy, _ = modoleaut32.FindProc("SafeArrayCopy") + procSafeArrayCopyData, _ = modoleaut32.FindProc("SafeArrayCopyData") + procSafeArrayCreate, _ = modoleaut32.FindProc("SafeArrayCreate") + procSafeArrayCreateEx, _ = modoleaut32.FindProc("SafeArrayCreateEx") + procSafeArrayCreateVector, _ = modoleaut32.FindProc("SafeArrayCreateVector") + procSafeArrayCreateVectorEx, _ = modoleaut32.FindProc("SafeArrayCreateVectorEx") + procSafeArrayDestroy, _ = modoleaut32.FindProc("SafeArrayDestroy") + procSafeArrayDestroyData, _ = modoleaut32.FindProc("SafeArrayDestroyData") + procSafeArrayDestroyDescriptor, _ = modoleaut32.FindProc("SafeArrayDestroyDescriptor") + procSafeArrayGetDim, _ = modoleaut32.FindProc("SafeArrayGetDim") + procSafeArrayGetElement, _ = modoleaut32.FindProc("SafeArrayGetElement") + procSafeArrayGetElemsize, _ = modoleaut32.FindProc("SafeArrayGetElemsize") + procSafeArrayGetIID, _ = modoleaut32.FindProc("SafeArrayGetIID") + procSafeArrayGetLBound, _ = modoleaut32.FindProc("SafeArrayGetLBound") + procSafeArrayGetUBound, _ = modoleaut32.FindProc("SafeArrayGetUBound") + procSafeArrayGetVartype, _ = modoleaut32.FindProc("SafeArrayGetVartype") + procSafeArrayLock, _ = modoleaut32.FindProc("SafeArrayLock") + procSafeArrayPtrOfIndex, _ = modoleaut32.FindProc("SafeArrayPtrOfIndex") + procSafeArrayUnaccessData, _ = modoleaut32.FindProc("SafeArrayUnaccessData") + procSafeArrayUnlock, _ = modoleaut32.FindProc("SafeArrayUnlock") + procSafeArrayPutElement, _ = modoleaut32.FindProc("SafeArrayPutElement") + //procSafeArrayRedim, _ = modoleaut32.FindProc("SafeArrayRedim") // TODO + //procSafeArraySetIID, _ = modoleaut32.FindProc("SafeArraySetIID") // TODO + procSafeArrayGetRecordInfo, _ = modoleaut32.FindProc("SafeArrayGetRecordInfo") + procSafeArraySetRecordInfo, _ = modoleaut32.FindProc("SafeArraySetRecordInfo") +) + +// safeArrayAccessData returns raw array pointer. +// +// AKA: SafeArrayAccessData in Windows API. +// Todo: Test +func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) { + err = convertHresultToError( + procSafeArrayAccessData.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&element)))) + return +} + +// safeArrayUnaccessData releases raw array. +// +// AKA: SafeArrayUnaccessData in Windows API. +func safeArrayUnaccessData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocData allocates SafeArray. +// +// AKA: SafeArrayAllocData in Windows API. +func safeArrayAllocData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayAllocDescriptor allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptor in Windows API. +func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayAllocDescriptorEx allocates SafeArray. +// +// AKA: SafeArrayAllocDescriptorEx in Windows API. +func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayAllocDescriptorEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopy returns copy of SafeArray. +// +// AKA: SafeArrayCopy in Windows API. +func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) { + err = convertHresultToError( + procSafeArrayCopy.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(&safearray)))) + return +} + +// safeArrayCopyData duplicates SafeArray into another SafeArray object. +// +// AKA: SafeArrayCopyData in Windows API. +func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) { + err = convertHresultToError( + procSafeArrayCopyData.Call( + uintptr(unsafe.Pointer(original)), + uintptr(unsafe.Pointer(duplicate)))) + return +} + +// safeArrayCreate creates SafeArray. +// +// AKA: SafeArrayCreate in Windows API. +func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreate.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds))) + safearray = (*SafeArray)(unsafe.Pointer(&sa)) + return +} + +// safeArrayCreateEx creates SafeArray. +// +// AKA: SafeArrayCreateEx in Windows API. +func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateEx.Call( + uintptr(variantType), + uintptr(dimensions), + uintptr(unsafe.Pointer(bounds)), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVector creates SafeArray. +// +// AKA: SafeArrayCreateVector in Windows API. +func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVector.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length)) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayCreateVectorEx creates SafeArray. +// +// AKA: SafeArrayCreateVectorEx in Windows API. +func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) { + sa, _, err := procSafeArrayCreateVectorEx.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length), + extra) + safearray = (*SafeArray)(unsafe.Pointer(sa)) + return +} + +// safeArrayDestroy destroys SafeArray object. +// +// AKA: SafeArrayDestroy in Windows API. +func safeArrayDestroy(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyData destroys SafeArray object. +// +// AKA: SafeArrayDestroyData in Windows API. +func safeArrayDestroyData(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayDestroyDescriptor destroys SafeArray object. +// +// AKA: SafeArrayDestroyDescriptor in Windows API. +func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayGetDim is the amount of dimensions in the SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetDim in Windows API. +func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) { + l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray))) + dimensions = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElementSize is the element size in bytes. +// +// AKA: SafeArrayGetElemsize in Windows API. +func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) { + l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray))) + length = (*uint32)(unsafe.Pointer(l)) + return +} + +// safeArrayGetElement retrieves element at given index. +func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error { + return convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(pv))) +} + +// safeArrayGetElementString retrieves element at given index and converts to string. +func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) { + var element *int16 + err = convertHresultToError( + procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(&element)))) + str = BstrToString(*(**uint16)(unsafe.Pointer(&element))) + SysFreeString(element) + return +} + +// safeArrayGetIID is the InterfaceID of the elements in the SafeArray. +// +// AKA: SafeArrayGetIID in Windows API. +func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) { + err = convertHresultToError( + procSafeArrayGetIID.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&guid)))) + return +} + +// safeArrayGetLBound returns lower bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetLBound in Windows API. +func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetLBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&lowerBound)))) + return +} + +// safeArrayGetUBound returns upper bounds of SafeArray. +// +// SafeArrays may have multiple dimensions. Meaning, it could be +// multidimensional array. +// +// AKA: SafeArrayGetUBound in Windows API. +func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) { + err = convertHresultToError( + procSafeArrayGetUBound.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(dimension), + uintptr(unsafe.Pointer(&upperBound)))) + return +} + +// safeArrayGetVartype returns data type of SafeArray. +// +// AKA: SafeArrayGetVartype in Windows API. +func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) { + err = convertHresultToError( + procSafeArrayGetVartype.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&varType)))) + return +} + +// safeArrayLock locks SafeArray for reading to modify SafeArray. +// +// This must be called during some calls to ensure that another process does not +// read or write to the SafeArray during editing. +// +// AKA: SafeArrayLock in Windows API. +func safeArrayLock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayUnlock unlocks SafeArray for reading. +// +// AKA: SafeArrayUnlock in Windows API. +func safeArrayUnlock(safearray *SafeArray) (err error) { + err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray)))) + return +} + +// safeArrayPutElement stores the data element at the specified location in the +// array. +// +// AKA: SafeArrayPutElement in Windows API. +func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) { + err = convertHresultToError( + procSafeArrayPutElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(unsafe.Pointer(element)))) + return +} + +// safeArrayGetRecordInfo accesses IRecordInfo info for custom types. +// +// AKA: SafeArrayGetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) { + err = convertHresultToError( + procSafeArrayGetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} + +// safeArraySetRecordInfo mutates IRecordInfo info for custom types. +// +// AKA: SafeArraySetRecordInfo in Windows API. +// +// XXX: Must implement IRecordInfo interface for this to return. +func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) { + err = convertHresultToError( + procSafeArraySetRecordInfo.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&recordInfo)))) + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/safearrayconversion.go b/cli/vendor/github.com/go-ole/go-ole/safearrayconversion.go new file mode 100644 index 0000000..259f488 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/safearrayconversion.go @@ -0,0 +1,140 @@ +// Helper for converting SafeArray to array of objects. + +package ole + +import ( + "unsafe" +) + +type SafeArrayConversion struct { + Array *SafeArray +} + +func (sac *SafeArrayConversion) ToStringArray() (strings []string) { + totalElements, _ := sac.TotalElements(0) + strings = make([]string, totalElements) + + for i := int32(0); i < totalElements; i++ { + strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i) + } + + return +} + +func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) { + totalElements, _ := sac.TotalElements(0) + bytes = make([]byte, totalElements) + + for i := int32(0); i < totalElements; i++ { + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)])) + } + + return +} + +func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) { + totalElements, _ := sac.TotalElements(0) + values = make([]interface{}, totalElements) + vt, _ := safeArrayGetVartype(sac.Array) + + for i := int32(0); i < totalElements; i++ { + switch VT(vt) { + case VT_BOOL: + var v bool + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I1: + var v int8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I2: + var v int16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I4: + var v int32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_I8: + var v int64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI1: + var v uint8 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI2: + var v uint16 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI4: + var v uint32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_UI8: + var v uint64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R4: + var v float32 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_R8: + var v float64 + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_BSTR: + var v string + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v + case VT_VARIANT: + var v VARIANT + safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v)) + values[i] = v.Value() + default: + // TODO + } + } + + return +} + +func (sac *SafeArrayConversion) GetType() (varType uint16, err error) { + return safeArrayGetVartype(sac.Array) +} + +func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) { + return safeArrayGetDim(sac.Array) +} + +func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) { + return safeArrayGetElementSize(sac.Array) +} + +func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) { + if index < 1 { + index = 1 + } + + // Get array bounds + var LowerBounds int32 + var UpperBounds int32 + + LowerBounds, err = safeArrayGetLBound(sac.Array, index) + if err != nil { + return + } + + UpperBounds, err = safeArrayGetUBound(sac.Array, index) + if err != nil { + return + } + + totalElements = UpperBounds - LowerBounds + 1 + return +} + +// Release Safe Array memory +func (sac *SafeArrayConversion) Release() { + safeArrayDestroy(sac.Array) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/safearrayslices.go b/cli/vendor/github.com/go-ole/go-ole/safearrayslices.go new file mode 100644 index 0000000..a9fa885 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/safearrayslices.go @@ -0,0 +1,33 @@ +// +build windows + +package ole + +import ( + "unsafe" +) + +func safeArrayFromByteSlice(slice []byte) *SafeArray { + array, _ := safeArrayCreateVector(VT_UI1, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []byte to SAFEARRAY") + } + + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(&v))) + } + return array +} + +func safeArrayFromStringSlice(slice []string) *SafeArray { + array, _ := safeArrayCreateVector(VT_BSTR, 0, uint32(len(slice))) + + if array == nil { + panic("Could not convert []string to SAFEARRAY") + } + // SysAllocStringLen(s) + for i, v := range slice { + safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(SysAllocStringLen(v)))) + } + return array +} diff --git a/cli/vendor/github.com/go-ole/go-ole/utility.go b/cli/vendor/github.com/go-ole/go-ole/utility.go new file mode 100644 index 0000000..99ee82d --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/utility.go @@ -0,0 +1,101 @@ +package ole + +import ( + "unicode/utf16" + "unsafe" +) + +// ClassIDFrom retrieves class ID whether given is program ID or application string. +// +// Helper that provides check against both Class ID from Program ID and Class ID from string. It is +// faster, if you know which you are using, to use the individual functions, but this will check +// against available functions for you. +func ClassIDFrom(programID string) (classID *GUID, err error) { + classID, err = CLSIDFromProgID(programID) + if err != nil { + classID, err = CLSIDFromString(programID) + if err != nil { + return + } + } + return +} + +// BytePtrToString converts byte pointer to a Go string. +func BytePtrToString(p *byte) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +// UTF16PtrToString is alias for LpOleStrToString. +// +// Kept for compatibility reasons. +func UTF16PtrToString(p *uint16) string { + return LpOleStrToString(p) +} + +// LpOleStrToString converts COM Unicode to Go string. +func LpOleStrToString(p *uint16) string { + if p == nil { + return "" + } + + length := lpOleStrLen(p) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + + return string(utf16.Decode(a)) +} + +// BstrToString converts COM binary string to Go string. +func BstrToString(p *uint16) string { + if p == nil { + return "" + } + length := SysStringLen((*int16)(unsafe.Pointer(p))) + a := make([]uint16, length) + + ptr := unsafe.Pointer(p) + + for i := 0; i < int(length); i++ { + a[i] = *(*uint16)(ptr) + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return string(utf16.Decode(a)) +} + +// lpOleStrLen returns the length of Unicode string. +func lpOleStrLen(p *uint16) (length int64) { + if p == nil { + return 0 + } + + ptr := unsafe.Pointer(p) + + for i := 0; ; i++ { + if 0 == *(*uint16)(ptr) { + length = int64(i) + break + } + ptr = unsafe.Pointer(uintptr(ptr) + 2) + } + return +} + +// convertHresultToError converts syscall to error, if call is unsuccessful. +func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) { + if hr != 0 { + err = NewError(hr) + } + return +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variables.go b/cli/vendor/github.com/go-ole/go-ole/variables.go new file mode 100644 index 0000000..ebe00f1 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variables.go @@ -0,0 +1,16 @@ +// +build windows + +package ole + +import ( + "syscall" +) + +var ( + modcombase = syscall.NewLazyDLL("combase.dll") + modkernel32, _ = syscall.LoadDLL("kernel32.dll") + modole32, _ = syscall.LoadDLL("ole32.dll") + modoleaut32, _ = syscall.LoadDLL("oleaut32.dll") + modmsvcrt, _ = syscall.LoadDLL("msvcrt.dll") + moduser32, _ = syscall.LoadDLL("user32.dll") +) diff --git a/cli/vendor/github.com/go-ole/go-ole/variant.go b/cli/vendor/github.com/go-ole/go-ole/variant.go new file mode 100644 index 0000000..967a23f --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant.go @@ -0,0 +1,105 @@ +package ole + +import "unsafe" + +// NewVariant returns new variant based on type and value. +func NewVariant(vt VT, val int64) VARIANT { + return VARIANT{VT: vt, Val: val} +} + +// ToIUnknown converts Variant to Unknown object. +func (v *VARIANT) ToIUnknown() *IUnknown { + if v.VT != VT_UNKNOWN { + return nil + } + return (*IUnknown)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToIDispatch converts variant to dispatch object. +func (v *VARIANT) ToIDispatch() *IDispatch { + if v.VT != VT_DISPATCH { + return nil + } + return (*IDispatch)(unsafe.Pointer(uintptr(v.Val))) +} + +// ToArray converts variant to SafeArray helper. +func (v *VARIANT) ToArray() *SafeArrayConversion { + if v.VT != VT_SAFEARRAY { + if v.VT&VT_ARRAY == 0 { + return nil + } + } + var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val))) + return &SafeArrayConversion{safeArray} +} + +// ToString converts variant to Go string. +func (v *VARIANT) ToString() string { + if v.VT != VT_BSTR { + return "" + } + return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val))) +} + +// Clear the memory of variant object. +func (v *VARIANT) Clear() error { + return VariantClear(v) +} + +// Value returns variant value based on its type. +// +// Currently supported types: 2- and 4-byte integers, strings, bools. +// Note that 64-bit integers, datetimes, and other types are stored as strings +// and will be returned as strings. +// +// Needs to be further converted, because this returns an interface{}. +func (v *VARIANT) Value() interface{} { + switch v.VT { + case VT_I1: + return int8(v.Val) + case VT_UI1: + return uint8(v.Val) + case VT_I2: + return int16(v.Val) + case VT_UI2: + return uint16(v.Val) + case VT_I4: + return int32(v.Val) + case VT_UI4: + return uint32(v.Val) + case VT_I8: + return int64(v.Val) + case VT_UI8: + return uint64(v.Val) + case VT_INT: + return int(v.Val) + case VT_UINT: + return uint(v.Val) + case VT_INT_PTR: + return uintptr(v.Val) // TODO + case VT_UINT_PTR: + return uintptr(v.Val) + case VT_R4: + return *(*float32)(unsafe.Pointer(&v.Val)) + case VT_R8: + return *(*float64)(unsafe.Pointer(&v.Val)) + case VT_BSTR: + return v.ToString() + case VT_DATE: + // VT_DATE type will either return float64 or time.Time. + d := uint64(v.Val) + date, err := GetVariantDate(d) + if err != nil { + return float64(v.Val) + } + return date + case VT_UNKNOWN: + return v.ToIUnknown() + case VT_DISPATCH: + return v.ToIDispatch() + case VT_BOOL: + return v.Val != 0 + } + return nil +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_386.go b/cli/vendor/github.com/go-ole/go-ole/variant_386.go new file mode 100644 index 0000000..e73736b --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_386.go @@ -0,0 +1,11 @@ +// +build 386 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_amd64.go b/cli/vendor/github.com/go-ole/go-ole/variant_amd64.go new file mode 100644 index 0000000..dccdde1 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_amd64.go @@ -0,0 +1,12 @@ +// +build amd64 + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_date_386.go b/cli/vendor/github.com/go-ole/go-ole/variant_date_386.go new file mode 100644 index 0000000..1b970f6 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_date_386.go @@ -0,0 +1,22 @@ +// +build windows,386 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + v1 := uint32(value) + v2 := uint32(value >> 32) + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_date_amd64.go b/cli/vendor/github.com/go-ole/go-ole/variant_date_amd64.go new file mode 100644 index 0000000..6952f1f --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_date_amd64.go @@ -0,0 +1,20 @@ +// +build windows,amd64 + +package ole + +import ( + "errors" + "syscall" + "time" + "unsafe" +) + +// GetVariantDate converts COM Variant Time value to Go time.Time. +func GetVariantDate(value uint64) (time.Time, error) { + var st syscall.Systemtime + r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st))) + if r != 0 { + return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil + } + return time.Now(), errors.New("Could not convert to time, passing current time.") +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_ppc64le.go b/cli/vendor/github.com/go-ole/go-ole/variant_ppc64le.go new file mode 100644 index 0000000..326427a --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_ppc64le.go @@ -0,0 +1,12 @@ +// +build ppc64le + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/cli/vendor/github.com/go-ole/go-ole/variant_s390x.go b/cli/vendor/github.com/go-ole/go-ole/variant_s390x.go new file mode 100644 index 0000000..9874ca6 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/variant_s390x.go @@ -0,0 +1,12 @@ +// +build s390x + +package ole + +type VARIANT struct { + VT VT // 2 + wReserved1 uint16 // 4 + wReserved2 uint16 // 6 + wReserved3 uint16 // 8 + Val int64 // 16 + _ [8]byte // 24 +} diff --git a/cli/vendor/github.com/go-ole/go-ole/vt_string.go b/cli/vendor/github.com/go-ole/go-ole/vt_string.go new file mode 100644 index 0000000..729b4a0 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/vt_string.go @@ -0,0 +1,58 @@ +// generated by stringer -output vt_string.go -type VT; DO NOT EDIT + +package ole + +import "fmt" + +const ( + _VT_name_0 = "VT_EMPTYVT_NULLVT_I2VT_I4VT_R4VT_R8VT_CYVT_DATEVT_BSTRVT_DISPATCHVT_ERRORVT_BOOLVT_VARIANTVT_UNKNOWNVT_DECIMAL" + _VT_name_1 = "VT_I1VT_UI1VT_UI2VT_UI4VT_I8VT_UI8VT_INTVT_UINTVT_VOIDVT_HRESULTVT_PTRVT_SAFEARRAYVT_CARRAYVT_USERDEFINEDVT_LPSTRVT_LPWSTR" + _VT_name_2 = "VT_RECORDVT_INT_PTRVT_UINT_PTR" + _VT_name_3 = "VT_FILETIMEVT_BLOBVT_STREAMVT_STORAGEVT_STREAMED_OBJECTVT_STORED_OBJECTVT_BLOB_OBJECTVT_CFVT_CLSID" + _VT_name_4 = "VT_BSTR_BLOBVT_VECTOR" + _VT_name_5 = "VT_ARRAY" + _VT_name_6 = "VT_BYREF" + _VT_name_7 = "VT_RESERVED" + _VT_name_8 = "VT_ILLEGAL" +) + +var ( + _VT_index_0 = [...]uint8{0, 8, 15, 20, 25, 30, 35, 40, 47, 54, 65, 73, 80, 90, 100, 110} + _VT_index_1 = [...]uint8{0, 5, 11, 17, 23, 28, 34, 40, 47, 54, 64, 70, 82, 91, 105, 113, 122} + _VT_index_2 = [...]uint8{0, 9, 19, 30} + _VT_index_3 = [...]uint8{0, 11, 18, 27, 37, 55, 71, 85, 90, 98} + _VT_index_4 = [...]uint8{0, 12, 21} + _VT_index_5 = [...]uint8{0, 8} + _VT_index_6 = [...]uint8{0, 8} + _VT_index_7 = [...]uint8{0, 11} + _VT_index_8 = [...]uint8{0, 10} +) + +func (i VT) String() string { + switch { + case 0 <= i && i <= 14: + return _VT_name_0[_VT_index_0[i]:_VT_index_0[i+1]] + case 16 <= i && i <= 31: + i -= 16 + return _VT_name_1[_VT_index_1[i]:_VT_index_1[i+1]] + case 36 <= i && i <= 38: + i -= 36 + return _VT_name_2[_VT_index_2[i]:_VT_index_2[i+1]] + case 64 <= i && i <= 72: + i -= 64 + return _VT_name_3[_VT_index_3[i]:_VT_index_3[i+1]] + case 4095 <= i && i <= 4096: + i -= 4095 + return _VT_name_4[_VT_index_4[i]:_VT_index_4[i+1]] + case i == 8192: + return _VT_name_5 + case i == 16384: + return _VT_name_6 + case i == 32768: + return _VT_name_7 + case i == 65535: + return _VT_name_8 + default: + return fmt.Sprintf("VT(%d)", i) + } +} diff --git a/cli/vendor/github.com/go-ole/go-ole/winrt.go b/cli/vendor/github.com/go-ole/go-ole/winrt.go new file mode 100644 index 0000000..4e9eca7 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/winrt.go @@ -0,0 +1,99 @@ +// +build windows + +package ole + +import ( + "reflect" + "syscall" + "unicode/utf8" + "unsafe" +) + +var ( + procRoInitialize = modcombase.NewProc("RoInitialize") + procRoActivateInstance = modcombase.NewProc("RoActivateInstance") + procRoGetActivationFactory = modcombase.NewProc("RoGetActivationFactory") + procWindowsCreateString = modcombase.NewProc("WindowsCreateString") + procWindowsDeleteString = modcombase.NewProc("WindowsDeleteString") + procWindowsGetStringRawBuffer = modcombase.NewProc("WindowsGetStringRawBuffer") +) + +func RoInitialize(thread_type uint32) (err error) { + hr, _, _ := procRoInitialize.Call(uintptr(thread_type)) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoActivateInstance.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + hClsid, err := NewHString(clsid) + if err != nil { + return nil, err + } + defer DeleteHString(hClsid) + + hr, _, _ := procRoGetActivationFactory.Call( + uintptr(unsafe.Pointer(hClsid)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&ins))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + u16 := syscall.StringToUTF16Ptr(s) + len := uint32(utf8.RuneCountInString(s)) + hr, _, _ := procWindowsCreateString.Call( + uintptr(unsafe.Pointer(u16)), + uintptr(len), + uintptr(unsafe.Pointer(&hstring))) + if hr != 0 { + err = NewError(hr) + } + return +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + hr, _, _ := procWindowsDeleteString.Call(uintptr(hstring)) + if hr != 0 { + err = NewError(hr) + } + return +} + +// String returns Go string value of HString. +func (h HString) String() string { + var u16buf uintptr + var u16len uint32 + u16buf, _, _ = procWindowsGetStringRawBuffer.Call( + uintptr(h), + uintptr(unsafe.Pointer(&u16len))) + + u16hdr := reflect.SliceHeader{Data: u16buf, Len: int(u16len), Cap: int(u16len)} + u16 := *(*[]uint16)(unsafe.Pointer(&u16hdr)) + return syscall.UTF16ToString(u16) +} diff --git a/cli/vendor/github.com/go-ole/go-ole/winrt_doc.go b/cli/vendor/github.com/go-ole/go-ole/winrt_doc.go new file mode 100644 index 0000000..52e6d74 --- /dev/null +++ b/cli/vendor/github.com/go-ole/go-ole/winrt_doc.go @@ -0,0 +1,36 @@ +// +build !windows + +package ole + +// RoInitialize +func RoInitialize(thread_type uint32) (err error) { + return NewError(E_NOTIMPL) +} + +// RoActivateInstance +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// RoGetActivationFactory +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + return HString(uintptr(0)), NewError(E_NOTIMPL) +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + return NewError(E_NOTIMPL) +} + +// String returns Go string value of HString. +func (h HString) String() string { + return "" +} diff --git a/cli/vendor/github.com/godbus/dbus/v5/.travis.yml b/cli/vendor/github.com/godbus/dbus/v5/.travis.yml new file mode 100644 index 0000000..dd67672 --- /dev/null +++ b/cli/vendor/github.com/godbus/dbus/v5/.travis.yml @@ -0,0 +1,50 @@ +dist: bionic +language: go +go_import_path: github.com/godbus/dbus + +go: + - 1.11.x + - 1.12.x + - 1.13.x + - tip + +matrix: + fast_finish: true + allow_failures: + - go: tip + +addons: + apt: + packages: + - dbus + - dbus-x11 + +before_install: + - export GO111MODULE=on + +script: + - go test -v -race -mod=readonly ./... # Run all the tests with the race detector enabled + - go vet ./... # go vet is the official Go static analyzer + +jobs: + include: + # The build matrix doesn't cover build stages, so manually expand + # the jobs with anchors + - &multiarch + stage: "Multiarch Test" + go: 1.11.x + env: TARGETS="386 arm arm64 ppc64le" + before_install: + - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + script: + - | + set -e + for target in $TARGETS; do + printf "\e[1mRunning test suite under ${target}.\e[0m\n" + GOARCH="$target" go test -v ./... + printf "\n\n" + done + - <<: *multiarch + go: 1.12.x + - <<: *multiarch + go: 1.13.x diff --git a/cli/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md b/cli/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md new file mode 100644 index 0000000..c88f9b2 --- /dev/null +++ b/cli/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# How to Contribute + +## Getting Started + +- Fork the repository on GitHub +- Read the [README](README.markdown) for build and test instructions +- Play with the project, submit bugs, submit patches! + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work (usually master). +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Make sure the tests pass, and add any new tests as appropriate. +- Submit a pull request to the original repository. + +Thanks for your contributions! + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that you can easily kill and +start for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +