refactor(deps): Manage deps with goven
This commit is contained in:
8
third_party/github.com/coreos/go-systemd/.travis.yml
vendored
Normal file
8
third_party/github.com/coreos/go-systemd/.travis.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
language: go
|
||||
go: 1.2
|
||||
|
||||
install:
|
||||
- echo "Skip install"
|
||||
|
||||
script:
|
||||
- ./test
|
||||
191
third_party/github.com/coreos/go-systemd/LICENSE
vendored
Normal file
191
third_party/github.com/coreos/go-systemd/LICENSE
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
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:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
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
|
||||
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.
|
||||
44
third_party/github.com/coreos/go-systemd/README.md
vendored
Normal file
44
third_party/github.com/coreos/go-systemd/README.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# go-systemd
|
||||
|
||||
Go bindings to systemd. The project has three packages:
|
||||
|
||||
- activation - for writing and using socket activation from Go
|
||||
- journal - for writing to systemd's logging service, journal
|
||||
- dbus - for starting/stopping/inspecting running services and units
|
||||
|
||||
Go docs for the entire project are here:
|
||||
|
||||
http://godoc.org/github.com/coreos/go-systemd
|
||||
|
||||
## Socket Activation
|
||||
|
||||
An example HTTP server using socket activation can be quickly setup by
|
||||
following this README on a Linux machine running systemd:
|
||||
|
||||
https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
|
||||
|
||||
## Journal
|
||||
|
||||
Using this package you can submit journal entries directly to systemd's journal taking advantage of features like indexed key/value pairs for each log entry.
|
||||
|
||||
## D-Bus
|
||||
|
||||
The D-Bus API lets you start, stop and introspect systemd units. The API docs are here:
|
||||
|
||||
http://godoc.org/github.com/coreos/go-systemd/dbus
|
||||
|
||||
### Debugging
|
||||
|
||||
Create `/etc/dbus-1/system-local.conf` that looks like this:
|
||||
|
||||
```
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow eavesdrop="true"/>
|
||||
<allow eavesdrop="true" send_destination="*"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
```
|
||||
56
third_party/github.com/coreos/go-systemd/activation/files.go
vendored
Normal file
56
third_party/github.com/coreos/go-systemd/activation/files.go
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package activation implements primitives for systemd socket activation.
|
||||
package activation
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// based on: https://gist.github.com/alberts/4640792
|
||||
const (
|
||||
listenFdsStart = 3
|
||||
)
|
||||
|
||||
func Files(unsetEnv bool) []*os.File {
|
||||
if unsetEnv {
|
||||
// there is no way to unset env in golang os package for now
|
||||
// https://code.google.com/p/go/issues/detail?id=6423
|
||||
defer os.Setenv("LISTEN_PID", "")
|
||||
defer os.Setenv("LISTEN_FDS", "")
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
|
||||
if err != nil || pid != os.Getpid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
|
||||
if err != nil || nfds == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var files []*os.File
|
||||
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
|
||||
syscall.CloseOnExec(fd)
|
||||
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
84
third_party/github.com/coreos/go-systemd/activation/files_test.go
vendored
Normal file
84
third_party/github.com/coreos/go-systemd/activation/files_test.go
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package activation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// correctStringWritten fails the text if the correct string wasn't written
|
||||
// to the other side of the pipe.
|
||||
func correctStringWritten(t *testing.T, r *os.File, expected string) bool {
|
||||
bytes := make([]byte, len(expected))
|
||||
io.ReadAtLeast(r, bytes, len(expected))
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Unexpected string %s", string(bytes))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TestActivation forks out a copy of activation.go example and reads back two
|
||||
// strings from the pipes that are passed in.
|
||||
func TestActivation(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
|
||||
r1, w1, _ := os.Pipe()
|
||||
r2, w2, _ := os.Pipe()
|
||||
cmd.ExtraFiles = []*os.File{
|
||||
w1,
|
||||
w2,
|
||||
}
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
correctStringWritten(t, r1, "Hello world")
|
||||
correctStringWritten(t, r2, "Goodbye world")
|
||||
}
|
||||
|
||||
func TestActivationNoFix(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2")
|
||||
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if bytes.Contains(out, []byte("No files")) == false {
|
||||
t.Fatalf("Child didn't error out as expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActivationNoFiles(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=0", "FIX_LISTEN_PID=1")
|
||||
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if bytes.Contains(out, []byte("No files")) == false {
|
||||
t.Fatalf("Child didn't error out as expected")
|
||||
}
|
||||
}
|
||||
38
third_party/github.com/coreos/go-systemd/activation/listeners.go
vendored
Normal file
38
third_party/github.com/coreos/go-systemd/activation/listeners.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2014 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package activation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Listeners returns net.Listeners for all socket activated fds passed to this process.
|
||||
func Listeners(unsetEnv bool) ([]net.Listener, error) {
|
||||
files := Files(unsetEnv)
|
||||
listeners := make([]net.Listener, len(files))
|
||||
|
||||
for i, f := range files {
|
||||
var err error
|
||||
listeners[i], err = net.FileListener(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up FileListener for fd %d: %s", f.Fd(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return listeners, nil
|
||||
}
|
||||
88
third_party/github.com/coreos/go-systemd/activation/listeners_test.go
vendored
Normal file
88
third_party/github.com/coreos/go-systemd/activation/listeners_test.go
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2014 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package activation
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// correctStringWritten fails the text if the correct string wasn't written
|
||||
// to the other side of the pipe.
|
||||
func correctStringWrittenNet(t *testing.T, r net.Conn, expected string) bool {
|
||||
bytes := make([]byte, len(expected))
|
||||
io.ReadAtLeast(r, bytes, len(expected))
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Unexpected string %s", string(bytes))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TestActivation forks out a copy of activation.go example and reads back two
|
||||
// strings from the pipes that are passed in.
|
||||
func TestListeners(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/listen.go")
|
||||
|
||||
l1, err := net.Listen("tcp", ":9999")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
l2, err := net.Listen("tcp", ":1234")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
t1 := l1.(*net.TCPListener)
|
||||
t2 := l2.(*net.TCPListener)
|
||||
|
||||
f1, _ := t1.File()
|
||||
f2, _ := t2.File()
|
||||
|
||||
cmd.ExtraFiles = []*os.File{
|
||||
f1,
|
||||
f2,
|
||||
}
|
||||
|
||||
r1, err := net.Dial("tcp", "127.0.0.1:9999")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r1.Write([]byte("Hi"))
|
||||
|
||||
r2, err := net.Dial("tcp", "127.0.0.1:1234")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r2.Write([]byte("Hi"))
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
||||
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
println(string(out))
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
correctStringWrittenNet(t, r1, "Hello world")
|
||||
correctStringWrittenNet(t, r2, "Goodbye world")
|
||||
}
|
||||
97
third_party/github.com/coreos/go-systemd/dbus/dbus.go
vendored
Normal file
97
third_party/github.com/coreos/go-systemd/dbus/dbus.go
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
)
|
||||
|
||||
const signalBuffer = 100
|
||||
|
||||
// ObjectPath creates a dbus.ObjectPath using the rules that systemd uses for
|
||||
// serializing special characters.
|
||||
func ObjectPath(path string) dbus.ObjectPath {
|
||||
path = strings.Replace(path, ".", "_2e", -1)
|
||||
path = strings.Replace(path, "-", "_2d", -1)
|
||||
path = strings.Replace(path, "@", "_40", -1)
|
||||
|
||||
return dbus.ObjectPath(path)
|
||||
}
|
||||
|
||||
// Conn is a connection to systemds dbus endpoint.
|
||||
type Conn struct {
|
||||
sysconn *dbus.Conn
|
||||
sysobj *dbus.Object
|
||||
jobListener struct {
|
||||
jobs map[dbus.ObjectPath]chan string
|
||||
sync.Mutex
|
||||
}
|
||||
subscriber struct {
|
||||
updateCh chan<- *SubStateUpdate
|
||||
errCh chan<- error
|
||||
sync.Mutex
|
||||
ignore map[dbus.ObjectPath]int64
|
||||
cleanIgnore int64
|
||||
}
|
||||
dispatch map[string]func(dbus.Signal)
|
||||
}
|
||||
|
||||
// New() establishes a connection to the system bus and authenticates.
|
||||
func New() (*Conn, error) {
|
||||
c := new(Conn)
|
||||
|
||||
if err := c.initConnection(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.initJobs()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Conn) initConnection() error {
|
||||
var err error
|
||||
c.sysconn, err = dbus.SystemBusPrivate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.sysconn.Auth(nil)
|
||||
if err != nil {
|
||||
c.sysconn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.sysconn.Hello()
|
||||
if err != nil {
|
||||
c.sysconn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.sysobj = c.sysconn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||
|
||||
// Setup the listeners on jobs so that we can get completions
|
||||
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
|
||||
c.initSubscription()
|
||||
c.initDispatch()
|
||||
|
||||
return nil
|
||||
}
|
||||
41
third_party/github.com/coreos/go-systemd/dbus/dbus_test.go
vendored
Normal file
41
third_party/github.com/coreos/go-systemd/dbus/dbus_test.go
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestObjectPath ensures path encoding of the systemd rules works.
|
||||
func TestObjectPath(t *testing.T) {
|
||||
input := "/silly-path/to@a/unit..service"
|
||||
output := ObjectPath(input)
|
||||
expected := "/silly_2dpath/to_40a/unit_2e_2eservice"
|
||||
|
||||
if string(output) != expected {
|
||||
t.Fatalf("Output '%s' did not match expected '%s'", output, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNew ensures that New() works without errors.
|
||||
func TestNew(t *testing.T) {
|
||||
_, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
260
third_party/github.com/coreos/go-systemd/dbus/methods.go
vendored
Normal file
260
third_party/github.com/coreos/go-systemd/dbus/methods.go
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
)
|
||||
|
||||
func (c *Conn) initJobs() {
|
||||
c.jobListener.jobs = make(map[dbus.ObjectPath]chan string)
|
||||
}
|
||||
|
||||
func (c *Conn) jobComplete(signal *dbus.Signal) {
|
||||
var id uint32
|
||||
var job dbus.ObjectPath
|
||||
var unit string
|
||||
var result string
|
||||
dbus.Store(signal.Body, &id, &job, &unit, &result)
|
||||
c.jobListener.Lock()
|
||||
out, ok := c.jobListener.jobs[job]
|
||||
if ok {
|
||||
out <- result
|
||||
}
|
||||
c.jobListener.Unlock()
|
||||
}
|
||||
|
||||
func (c *Conn) startJob(job string, args ...interface{}) (<-chan string, error) {
|
||||
c.jobListener.Lock()
|
||||
defer c.jobListener.Unlock()
|
||||
|
||||
ch := make(chan string, 1)
|
||||
var path dbus.ObjectPath
|
||||
err := c.sysobj.Call(job, 0, args...).Store(&path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.jobListener.jobs[path] = ch
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (c *Conn) runJob(job string, args ...interface{}) (string, error) {
|
||||
respCh, err := c.startJob(job, args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return <-respCh, nil
|
||||
}
|
||||
|
||||
// StartUnit enqeues a start job and depending jobs, if any (unless otherwise
|
||||
// specified by the mode string).
|
||||
//
|
||||
// Takes the unit to activate, plus a mode string. The mode needs to be one of
|
||||
// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
|
||||
// "replace" the call will start the unit and its dependencies, possibly
|
||||
// replacing already queued jobs that conflict with this. If "fail" the call
|
||||
// will start the unit and its dependencies, but will fail if this would change
|
||||
// an already queued job. If "isolate" the call will start the unit in question
|
||||
// and terminate all units that aren't dependencies of it. If
|
||||
// "ignore-dependencies" it will start a unit but ignore all its dependencies.
|
||||
// If "ignore-requirements" it will start a unit but only ignore the
|
||||
// requirement dependencies. It is not recommended to make use of the latter
|
||||
// two options.
|
||||
//
|
||||
// Result string: one of done, canceled, timeout, failed, dependency, skipped.
|
||||
// done indicates successful execution of a job. canceled indicates that a job
|
||||
// has been canceled before it finished execution. timeout indicates that the
|
||||
// job timeout was reached. failed indicates that the job failed. dependency
|
||||
// indicates that a job this job has been depending on failed and the job hence
|
||||
// has been removed too. skipped indicates that a job was skipped because it
|
||||
// didn't apply to the units current state.
|
||||
func (c *Conn) StartUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.StartUnit", name, mode)
|
||||
}
|
||||
|
||||
// StopUnit is similar to StartUnit but stops the specified unit rather
|
||||
// than starting it.
|
||||
func (c *Conn) StopUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.StopUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
|
||||
func (c *Conn) ReloadUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
|
||||
}
|
||||
|
||||
// RestartUnit restarts a service. If a service is restarted that isn't
|
||||
// running it will be started.
|
||||
func (c *Conn) RestartUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// TryRestartUnit is like RestartUnit, except that a service that isn't running
|
||||
// is not affected by the restart.
|
||||
func (c *Conn) TryRestartUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
|
||||
// otherwise.
|
||||
func (c *Conn) ReloadOrRestartUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
|
||||
// flavored restart otherwise.
|
||||
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// StartTransientUnit() may be used to create and start a transient unit, which
|
||||
// will be released as soon as it is not running or referenced anymore or the
|
||||
// system is rebooted. name is the unit name including suffix, and must be
|
||||
// unique. mode is the same as in StartUnit(), properties contains properties
|
||||
// of the unit.
|
||||
func (c *Conn) StartTransientUnit(name string, mode string, properties ...Property) (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
|
||||
}
|
||||
|
||||
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
|
||||
// processes are killed.
|
||||
func (c *Conn) KillUnit(name string, signal int32) {
|
||||
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||
}
|
||||
|
||||
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||
var err error
|
||||
var props map[string]dbus.Variant
|
||||
|
||||
path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit)
|
||||
if !path.IsValid() {
|
||||
return nil, errors.New("invalid unit name: " + unit)
|
||||
}
|
||||
|
||||
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, "org.freedesktop.systemd1.Unit").Store(&props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(props))
|
||||
for k, v := range props {
|
||||
out[k] = v.Value()
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListUnits returns an array with all currently loaded units. Note that
|
||||
// units may be known by multiple names at the same time, and hence there might
|
||||
// be more unit names loaded than actual units behind them.
|
||||
func (c *Conn) ListUnits() ([]UnitStatus, error) {
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
status := make([]UnitStatus, len(result))
|
||||
statusInterface := make([]interface{}, len(status))
|
||||
for i := range status {
|
||||
statusInterface[i] = &status[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, statusInterface...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
type UnitStatus struct {
|
||||
Name string // The primary unit name as string
|
||||
Description string // The human readable description string
|
||||
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
|
||||
ActiveState string // The active state (i.e. whether the unit is currently started or not)
|
||||
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
|
||||
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
|
||||
Path dbus.ObjectPath // The unit object path
|
||||
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
|
||||
JobType string // The job type as string
|
||||
JobPath dbus.ObjectPath // The job object path
|
||||
}
|
||||
|
||||
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||
// creating symlinks to them in /etc or /run).
|
||||
//
|
||||
// It takes a list of unit files to enable (either just file names or full
|
||||
// absolute paths if the unit files are residing outside the usual unit
|
||||
// search paths), and two booleans: the first controls whether the unit shall
|
||||
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||
// The second one controls whether symlinks pointing to other units shall
|
||||
// be replaced if necessary.
|
||||
//
|
||||
// This call returns one boolean and an array with the changes made. The
|
||||
// boolean signals whether the unit files contained any enablement
|
||||
// information (i.e. an [Install]) section. The changes list consists of
|
||||
// structures with three strings: the type of the change (one of symlink
|
||||
// or unlink), the file name of the symlink and the destination of the
|
||||
// symlink.
|
||||
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||
var carries_install_info bool
|
||||
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
changes := make([]EnableUnitFileChange, len(result))
|
||||
changesInterface := make([]interface{}, len(changes))
|
||||
for i := range changes {
|
||||
changesInterface[i] = &changes[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, changesInterface...)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
return carries_install_info, changes, nil
|
||||
}
|
||||
|
||||
type EnableUnitFileChange struct {
|
||||
Type string // Type of the change (one of symlink or unlink)
|
||||
Filename string // File name of the symlink
|
||||
Destination string // Destination of the symlink
|
||||
}
|
||||
|
||||
// Reload instructs systemd to scan for and reload unit files. This is
|
||||
// equivalent to a 'systemctl daemon-reload'.
|
||||
func (c *Conn) Reload() (string, error) {
|
||||
return c.runJob("org.freedesktop.systemd1.Manager.Reload")
|
||||
}
|
||||
213
third_party/github.com/coreos/go-systemd/dbus/methods_test.go
vendored
Normal file
213
third_party/github.com/coreos/go-systemd/dbus/methods_test.go
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupConn(t *testing.T) *Conn {
|
||||
conn, err := New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func setupUnit(target string, conn *Conn, t *testing.T) {
|
||||
// Blindly stop the unit in case it is running
|
||||
conn.StopUnit(target, "replace")
|
||||
|
||||
// Blindly remove the symlink in case it exists
|
||||
targetRun := filepath.Join("/run/systemd/system/", target)
|
||||
err := os.Remove(targetRun)
|
||||
|
||||
// 1. Enable the unit
|
||||
abs, err := filepath.Abs("../fixtures/" + target)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fixture := []string{abs}
|
||||
|
||||
install, changes, err := conn.EnableUnitFiles(fixture, true, true)
|
||||
|
||||
if install != false {
|
||||
t.Fatal("Install was true")
|
||||
}
|
||||
|
||||
if len(changes) < 1 {
|
||||
t.Fatal("Expected one change, got %v", changes)
|
||||
}
|
||||
|
||||
if changes[0].Filename != targetRun {
|
||||
t.Fatal("Unexpected target filename")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic unit starting and stopping works.
|
||||
func TestStartStopUnit(t *testing.T) {
|
||||
target := "start-stop.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
|
||||
// 2. Start the unit
|
||||
job, err := conn.StartUnit(target, "replace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done, %v", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
job, err = conn.StopUnit(target, "replace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
||||
// systems and ensures that one of its properties is valid.
|
||||
func TestGetUnitProperties(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "-.mount"
|
||||
|
||||
info, err := conn.GetUnitProperties(unit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
names := info["Wants"].([]string)
|
||||
|
||||
if len(names) < 1 {
|
||||
t.Fatal("/ is unwanted")
|
||||
}
|
||||
|
||||
if names[0] != "system.slice" {
|
||||
t.Fatal("unexpected wants for /")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
||||
// unit with an invalid name. This test should be run with --test.timeout set,
|
||||
// as a fail will manifest as GetUnitProperties hanging indefinitely.
|
||||
func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "//invalid#$^/"
|
||||
|
||||
_, err := conn.GetUnitProperties(unit)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic transient unit starting and stopping works.
|
||||
func TestStartStopTransientUnit(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
props := []Property{
|
||||
PropExecStart([]string{"/bin/sleep", "400"}, false),
|
||||
}
|
||||
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
|
||||
|
||||
// Start the unit
|
||||
job, err := conn.StartTransientUnit(target, "replace", props...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done, %v", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
job, err = conn.StopUnit(target, "replace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
211
third_party/github.com/coreos/go-systemd/dbus/properties.go
vendored
Normal file
211
third_party/github.com/coreos/go-systemd/dbus/properties.go
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
)
|
||||
|
||||
// From the systemd docs:
|
||||
//
|
||||
// The properties array of StartTransientUnit() may take many of the settings
|
||||
// that may also be configured in unit files. Not all parameters are currently
|
||||
// accepted though, but we plan to cover more properties with future release.
|
||||
// Currently you may set the Description, Slice and all dependency types of
|
||||
// units, as well as RemainAfterExit, ExecStart for service units,
|
||||
// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
|
||||
// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
|
||||
// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
|
||||
// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
|
||||
// directly to their counterparts in unit files and as normal D-Bus object
|
||||
// properties. The exception here is the PIDs field of scope units which is
|
||||
// used for construction of the scope only and specifies the initial PIDs to
|
||||
// add to the scope object.
|
||||
|
||||
type Property struct {
|
||||
Name string
|
||||
Value dbus.Variant
|
||||
}
|
||||
|
||||
type PropertyCollection struct {
|
||||
Name string
|
||||
Properties []Property
|
||||
}
|
||||
|
||||
type execStart struct {
|
||||
Path string // the binary path to execute
|
||||
Args []string // an array with all arguments to pass to the executed command, starting with argument 0
|
||||
UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
|
||||
}
|
||||
|
||||
// PropExecStart sets the ExecStart service property. The first argument is a
|
||||
// slice with the binary path to execute followed by the arguments to pass to
|
||||
// the executed command. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||
execStarts := []execStart{
|
||||
execStart{
|
||||
Path: command[0],
|
||||
Args: command,
|
||||
UncleanIsFailure: uncleanIsFailure,
|
||||
},
|
||||
}
|
||||
|
||||
return Property{
|
||||
Name: "ExecStart",
|
||||
Value: dbus.MakeVariant(execStarts),
|
||||
}
|
||||
}
|
||||
|
||||
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||
func PropRemainAfterExit(b bool) Property {
|
||||
return Property{
|
||||
Name: "RemainAfterExit",
|
||||
Value: dbus.MakeVariant(b),
|
||||
}
|
||||
}
|
||||
|
||||
// PropDescription sets the Description unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||
func PropDescription(desc string) Property {
|
||||
return Property{
|
||||
Name: "Description",
|
||||
Value: dbus.MakeVariant(desc),
|
||||
}
|
||||
}
|
||||
|
||||
func propDependency(name string, units []string) Property {
|
||||
return Property{
|
||||
Name: name,
|
||||
Value: dbus.MakeVariant(units),
|
||||
}
|
||||
}
|
||||
|
||||
// PropRequires sets the Requires unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
|
||||
func PropRequires(units ...string) Property {
|
||||
return propDependency("Requires", units)
|
||||
}
|
||||
|
||||
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
|
||||
func PropRequiresOverridable(units ...string) Property {
|
||||
return propDependency("RequiresOverridable", units)
|
||||
}
|
||||
|
||||
// PropRequisite sets the Requisite unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
|
||||
func PropRequisite(units ...string) Property {
|
||||
return propDependency("Requisite", units)
|
||||
}
|
||||
|
||||
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
|
||||
func PropRequisiteOverridable(units ...string) Property {
|
||||
return propDependency("RequisiteOverridable", units)
|
||||
}
|
||||
|
||||
// PropWants sets the Wants unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
|
||||
func PropWants(units ...string) Property {
|
||||
return propDependency("Wants", units)
|
||||
}
|
||||
|
||||
// PropBindsTo sets the BindsTo unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
|
||||
func PropBindsTo(units ...string) Property {
|
||||
return propDependency("BindsTo", units)
|
||||
}
|
||||
|
||||
// PropRequiredBy sets the RequiredBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
|
||||
func PropRequiredBy(units ...string) Property {
|
||||
return propDependency("RequiredBy", units)
|
||||
}
|
||||
|
||||
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
|
||||
func PropRequiredByOverridable(units ...string) Property {
|
||||
return propDependency("RequiredByOverridable", units)
|
||||
}
|
||||
|
||||
// PropWantedBy sets the WantedBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
|
||||
func PropWantedBy(units ...string) Property {
|
||||
return propDependency("WantedBy", units)
|
||||
}
|
||||
|
||||
// PropBoundBy sets the BoundBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
|
||||
func PropBoundBy(units ...string) Property {
|
||||
return propDependency("BoundBy", units)
|
||||
}
|
||||
|
||||
// PropConflicts sets the Conflicts unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
|
||||
func PropConflicts(units ...string) Property {
|
||||
return propDependency("Conflicts", units)
|
||||
}
|
||||
|
||||
// PropConflictedBy sets the ConflictedBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
|
||||
func PropConflictedBy(units ...string) Property {
|
||||
return propDependency("ConflictedBy", units)
|
||||
}
|
||||
|
||||
// PropBefore sets the Before unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
|
||||
func PropBefore(units ...string) Property {
|
||||
return propDependency("Before", units)
|
||||
}
|
||||
|
||||
// PropAfter sets the After unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
|
||||
func PropAfter(units ...string) Property {
|
||||
return propDependency("After", units)
|
||||
}
|
||||
|
||||
// PropOnFailure sets the OnFailure unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
|
||||
func PropOnFailure(units ...string) Property {
|
||||
return propDependency("OnFailure", units)
|
||||
}
|
||||
|
||||
// PropTriggers sets the Triggers unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
|
||||
func PropTriggers(units ...string) Property {
|
||||
return propDependency("Triggers", units)
|
||||
}
|
||||
|
||||
// PropTriggeredBy sets the TriggeredBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
|
||||
func PropTriggeredBy(units ...string) Property {
|
||||
return propDependency("TriggeredBy", units)
|
||||
}
|
||||
|
||||
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
|
||||
func PropPropagatesReloadTo(units ...string) Property {
|
||||
return propDependency("PropagatesReloadTo", units)
|
||||
}
|
||||
|
||||
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
|
||||
func PropRequiresMountsFor(units ...string) Property {
|
||||
return propDependency("RequiresMountsFor", units)
|
||||
}
|
||||
26
third_party/github.com/coreos/go-systemd/dbus/set.go
vendored
Normal file
26
third_party/github.com/coreos/go-systemd/dbus/set.go
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package dbus
|
||||
|
||||
type set struct {
|
||||
data map[string]bool
|
||||
}
|
||||
|
||||
func (s *set) Add(value string) {
|
||||
s.data[value] = true
|
||||
}
|
||||
|
||||
func (s *set) Remove(value string) {
|
||||
delete(s.data, value)
|
||||
}
|
||||
|
||||
func (s *set) Contains(value string) (exists bool) {
|
||||
_, exists = s.data[value]
|
||||
return
|
||||
}
|
||||
|
||||
func (s *set) Length() (int) {
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
func newSet() (*set) {
|
||||
return &set{make(map[string] bool)}
|
||||
}
|
||||
26
third_party/github.com/coreos/go-systemd/dbus/set_test.go
vendored
Normal file
26
third_party/github.com/coreos/go-systemd/dbus/set_test.go
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestBasicSetActions asserts that Add & Remove behavior is correct
|
||||
func TestBasicSetActions(t *testing.T) {
|
||||
s := newSet()
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
|
||||
s.Add("foo")
|
||||
|
||||
if !s.Contains("foo") {
|
||||
t.Fatal("set should contain 'foo'")
|
||||
}
|
||||
|
||||
s.Remove("foo")
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
}
|
||||
249
third_party/github.com/coreos/go-systemd/dbus/subscription.go
vendored
Normal file
249
third_party/github.com/coreos/go-systemd/dbus/subscription.go
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
cleanIgnoreInterval = int64(10 * time.Second)
|
||||
ignoreInterval = int64(30 * time.Millisecond)
|
||||
)
|
||||
|
||||
// Subscribe sets up this connection to subscribe to all systemd dbus events.
|
||||
// This is required before calling SubscribeUnits. When the connection closes
|
||||
// systemd will automatically stop sending signals so there is no need to
|
||||
// explicitly call Unsubscribe().
|
||||
func (c *Conn) Subscribe() error {
|
||||
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
|
||||
c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
|
||||
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||
if err != nil {
|
||||
c.sysconn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe this connection from systemd dbus events.
|
||||
func (c *Conn) Unsubscribe() error {
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
|
||||
if err != nil {
|
||||
c.sysconn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) initSubscription() {
|
||||
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
|
||||
}
|
||||
|
||||
func (c *Conn) initDispatch() {
|
||||
ch := make(chan *dbus.Signal, signalBuffer)
|
||||
|
||||
c.sysconn.Signal(ch)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
signal := <-ch
|
||||
switch signal.Name {
|
||||
case "org.freedesktop.systemd1.Manager.JobRemoved":
|
||||
c.jobComplete(signal)
|
||||
|
||||
unitName := signal.Body[2].(string)
|
||||
var unitPath dbus.ObjectPath
|
||||
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
|
||||
if unitPath != dbus.ObjectPath("") {
|
||||
c.sendSubStateUpdate(unitPath)
|
||||
}
|
||||
case "org.freedesktop.systemd1.Manager.UnitNew":
|
||||
c.sendSubStateUpdate(signal.Body[1].(dbus.ObjectPath))
|
||||
case "org.freedesktop.DBus.Properties.PropertiesChanged":
|
||||
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
|
||||
// we only care about SubState updates, which are a Unit property
|
||||
c.sendSubStateUpdate(signal.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Returns two unbuffered channels which will receive all changed units every
|
||||
// interval. Deleted units are sent as nil.
|
||||
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
|
||||
}
|
||||
|
||||
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
|
||||
// size of the channels, the comparison function for detecting changes and a filter
|
||||
// function for cutting down on the noise that your channel receives.
|
||||
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func (string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
old := make(map[string]*UnitStatus)
|
||||
statusChan := make(chan map[string]*UnitStatus, buffer)
|
||||
errChan := make(chan error, buffer)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
timerChan := time.After(interval)
|
||||
|
||||
units, err := c.ListUnits()
|
||||
if err == nil {
|
||||
cur := make(map[string]*UnitStatus)
|
||||
for i := range units {
|
||||
if filterUnit != nil && filterUnit(units[i].Name) {
|
||||
continue
|
||||
}
|
||||
cur[units[i].Name] = &units[i]
|
||||
}
|
||||
|
||||
// add all new or changed units
|
||||
changed := make(map[string]*UnitStatus)
|
||||
for n, u := range cur {
|
||||
if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
|
||||
changed[n] = u
|
||||
}
|
||||
delete(old, n)
|
||||
}
|
||||
|
||||
// add all deleted units
|
||||
for oldN := range old {
|
||||
changed[oldN] = nil
|
||||
}
|
||||
|
||||
old = cur
|
||||
|
||||
if len(changed) != 0 {
|
||||
statusChan <- changed
|
||||
}
|
||||
} else {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
<-timerChan
|
||||
}
|
||||
}()
|
||||
|
||||
return statusChan, errChan
|
||||
}
|
||||
|
||||
type SubStateUpdate struct {
|
||||
UnitName string
|
||||
SubState string
|
||||
}
|
||||
|
||||
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||
// Although this writes to updateCh on every state change, the reported state
|
||||
// may be more recent than the change that generated it (due to an unavoidable
|
||||
// race in the systemd dbus interface). That is, this method provides a good
|
||||
// way to keep a current view of all units' states, but is not guaranteed to
|
||||
// show every state transition they go through. Furthermore, state changes
|
||||
// will only be written to the channel with non-blocking writes. If updateCh
|
||||
// is full, it attempts to write an error to errCh; if errCh is full, the error
|
||||
// passes silently.
|
||||
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
|
||||
c.subscriber.Lock()
|
||||
defer c.subscriber.Unlock()
|
||||
c.subscriber.updateCh = updateCh
|
||||
c.subscriber.errCh = errCh
|
||||
}
|
||||
|
||||
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
||||
c.subscriber.Lock()
|
||||
defer c.subscriber.Unlock()
|
||||
if c.subscriber.updateCh == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.shouldIgnore(path) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := c.GetUnitProperties(string(path))
|
||||
if err != nil {
|
||||
select {
|
||||
case c.subscriber.errCh <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
name := info["Id"].(string)
|
||||
substate := info["SubState"].(string)
|
||||
|
||||
update := &SubStateUpdate{name, substate}
|
||||
select {
|
||||
case c.subscriber.updateCh <- update:
|
||||
default:
|
||||
select {
|
||||
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
c.updateIgnore(path, info)
|
||||
}
|
||||
|
||||
// The ignore functions work around a wart in the systemd dbus interface.
|
||||
// Requesting the properties of an unloaded unit will cause systemd to send a
|
||||
// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
|
||||
// properties on UnitNew (as that's the only indication of a new unit coming up
|
||||
// for the first time), we would enter an infinite loop if we did not attempt
|
||||
// to detect and ignore these spurious signals. The signal themselves are
|
||||
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
|
||||
// unloaded unit's signals for a short time after requesting its properties.
|
||||
// This means that we will miss e.g. a transient unit being restarted
|
||||
// *immediately* upon failure and also a transient unit being started
|
||||
// immediately after requesting its status (with systemctl status, for example,
|
||||
// because this causes a UnitNew signal to be sent which then causes us to fetch
|
||||
// the properties).
|
||||
|
||||
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
|
||||
t, ok := c.subscriber.ignore[path]
|
||||
return ok && t >= time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
|
||||
c.cleanIgnore()
|
||||
|
||||
// unit is unloaded - it will trigger bad systemd dbus behavior
|
||||
if info["LoadState"].(string) == "not-found" {
|
||||
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
|
||||
}
|
||||
}
|
||||
|
||||
// without this, ignore would grow unboundedly over time
|
||||
func (c *Conn) cleanIgnore() {
|
||||
now := time.Now().UnixNano()
|
||||
if c.subscriber.cleanIgnore < now {
|
||||
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
|
||||
|
||||
for p, t := range c.subscriber.ignore {
|
||||
if t < now {
|
||||
delete(c.subscriber.ignore, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
third_party/github.com/coreos/go-systemd/dbus/subscription_set.go
vendored
Normal file
32
third_party/github.com/coreos/go-systemd/dbus/subscription_set.go
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SubscriptionSet returns a subscription set which is like conn.Subscribe but
|
||||
// can filter to only return events for a set of units.
|
||||
type SubscriptionSet struct {
|
||||
*set
|
||||
conn *Conn
|
||||
}
|
||||
|
||||
|
||||
func (s *SubscriptionSet) filter(unit string) bool {
|
||||
return !s.Contains(unit)
|
||||
}
|
||||
|
||||
// Subscribe starts listening for dbus events for all of the units in the set.
|
||||
// Returns channels identical to conn.SubscribeUnits.
|
||||
func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
// TODO: Make fully evented by using systemd 209 with properties changed values
|
||||
return s.conn.SubscribeUnitsCustom(time.Second, 0,
|
||||
func(u1, u2 *UnitStatus) bool { return *u1 != *u2 },
|
||||
func(unit string) bool { return s.filter(unit) },
|
||||
)
|
||||
}
|
||||
|
||||
// NewSubscriptionSet returns a new subscription set.
|
||||
func (conn *Conn) NewSubscriptionSet() (*SubscriptionSet) {
|
||||
return &SubscriptionSet{newSet(), conn}
|
||||
}
|
||||
67
third_party/github.com/coreos/go-systemd/dbus/subscription_set_test.go
vendored
Normal file
67
third_party/github.com/coreos/go-systemd/dbus/subscription_set_test.go
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscriptionSetUnit(t *testing.T) {
|
||||
target := "subscribe-events-set.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
subSet := conn.NewSubscriptionSet()
|
||||
evChan, errChan := subSet.Subscribe()
|
||||
|
||||
subSet.Add(target)
|
||||
setupUnit(target, conn, t)
|
||||
|
||||
job, err := conn.StartUnit(target, "replace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
if !ok {
|
||||
t.Fatal("Unexpected event %v", changes)
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
90
third_party/github.com/coreos/go-systemd/dbus/subscription_test.go
vendored
Normal file
90
third_party/github.com/coreos/go-systemd/dbus/subscription_test.go
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribe exercises the basics of subscription
|
||||
func TestSubscribe(t *testing.T) {
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscribeUnit(t *testing.T) {
|
||||
target := "subscribe-events.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
evChan, errChan := conn.SubscribeUnits(time.Second)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
|
||||
job, err := conn.StartUnit(target, "replace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
// Just continue until we see our event.
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
44
third_party/github.com/coreos/go-systemd/examples/activation/activation.go
vendored
Normal file
44
third_party/github.com/coreos/go-systemd/examples/activation/activation.go
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Activation example used by the activation unit tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func fixListenPid() {
|
||||
if os.Getenv("FIX_LISTEN_PID") != "" {
|
||||
// HACK: real systemd would set LISTEN_PID before exec'ing but
|
||||
// this is too difficult in golang for the purpose of a test.
|
||||
// Do not do this in real code.
|
||||
os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fixListenPid()
|
||||
|
||||
files := activation.Files(false)
|
||||
|
||||
if len(files) == 0 {
|
||||
panic("No files")
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
|
||||
panic("Should not unset envs")
|
||||
}
|
||||
|
||||
files = activation.Files(true)
|
||||
|
||||
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
|
||||
panic("Can not unset envs")
|
||||
}
|
||||
|
||||
// Write out the expected strings to the two pipes
|
||||
files[0].Write([]byte("Hello world"))
|
||||
files[1].Write([]byte("Goodbye world"))
|
||||
|
||||
return
|
||||
}
|
||||
19
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/README.md
vendored
Normal file
19
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/README.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
## socket activated http server
|
||||
|
||||
This is a simple example of using socket activation with systemd to serve a
|
||||
simple HTTP server on http://127.0.0.1:8076
|
||||
|
||||
To try it out `go get` the httpserver and run it under the systemd-activate helper
|
||||
|
||||
```
|
||||
export GOPATH=`pwd`
|
||||
go get github.com/coreos/go-systemd/examples/activation/httpserver
|
||||
sudo /usr/lib/systemd/systemd-activate -l 127.0.0.1:8076 ./bin/httpserver
|
||||
```
|
||||
|
||||
Then curl the URL and you will notice that it starts up:
|
||||
|
||||
```
|
||||
curl 127.0.0.1:8076
|
||||
hello socket activated world!
|
||||
```
|
||||
11
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service
vendored
Normal file
11
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Hello World HTTP
|
||||
Requires=network.target
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/httpserver
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
5
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket
vendored
Normal file
5
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:8076
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
26
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go
vendored
Normal file
26
third_party/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func HelloServer(w http.ResponseWriter, req *http.Request) {
|
||||
io.WriteString(w, "hello socket activated world!\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
listeners, err := activation.Listeners(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(listeners) != 1 {
|
||||
panic("Unexpected number of socket activation fds")
|
||||
}
|
||||
|
||||
http.HandleFunc("/", HelloServer)
|
||||
http.Serve(listeners[0], nil)
|
||||
}
|
||||
50
third_party/github.com/coreos/go-systemd/examples/activation/listen.go
vendored
Normal file
50
third_party/github.com/coreos/go-systemd/examples/activation/listen.go
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Activation example used by the activation unit tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func fixListenPid() {
|
||||
if os.Getenv("FIX_LISTEN_PID") != "" {
|
||||
// HACK: real systemd would set LISTEN_PID before exec'ing but
|
||||
// this is too difficult in golang for the purpose of a test.
|
||||
// Do not do this in real code.
|
||||
os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fixListenPid()
|
||||
|
||||
listeners, _ := activation.Listeners(false)
|
||||
|
||||
if len(listeners) == 0 {
|
||||
panic("No listeners")
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
|
||||
panic("Should not unset envs")
|
||||
}
|
||||
|
||||
listeners, err := activation.Listeners(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
|
||||
panic("Can not unset envs")
|
||||
}
|
||||
|
||||
c0, _ := listeners[0].Accept()
|
||||
c1, _ := listeners[1].Accept()
|
||||
|
||||
// Write out the expected strings to the two pipes
|
||||
c0.Write([]byte("Hello world"))
|
||||
c1.Write([]byte("Goodbye world"))
|
||||
|
||||
return
|
||||
}
|
||||
5
third_party/github.com/coreos/go-systemd/fixtures/start-stop.service
vendored
Normal file
5
third_party/github.com/coreos/go-systemd/fixtures/start-stop.service
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
||||
5
third_party/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service
vendored
Normal file
5
third_party/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
||||
5
third_party/github.com/coreos/go-systemd/fixtures/subscribe-events.service
vendored
Normal file
5
third_party/github.com/coreos/go-systemd/fixtures/subscribe-events.service
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
||||
168
third_party/github.com/coreos/go-systemd/journal/send.go
vendored
Normal file
168
third_party/github.com/coreos/go-systemd/journal/send.go
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright 2013 CoreOS Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package journal provides write bindings to the systemd journal
|
||||
package journal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Priority of a journal message
|
||||
type Priority int
|
||||
|
||||
const (
|
||||
PriEmerg Priority = iota
|
||||
PriAlert
|
||||
PriCrit
|
||||
PriErr
|
||||
PriWarning
|
||||
PriNotice
|
||||
PriInfo
|
||||
PriDebug
|
||||
)
|
||||
|
||||
var conn net.Conn
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
|
||||
if err != nil {
|
||||
conn = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled returns true iff the systemd journal is available for logging
|
||||
func Enabled() bool {
|
||||
return conn != nil
|
||||
}
|
||||
|
||||
// Send a message to the systemd journal. vars is a map of journald fields to
|
||||
// values. Fields must be composed of uppercase letters, numbers, and
|
||||
// underscores, but must not start with an underscore. Within these
|
||||
// restrictions, any arbitrary field name may be used. Some names have special
|
||||
// significance: see the journalctl documentation
|
||||
// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
|
||||
// for more details. vars may be nil.
|
||||
func Send(message string, priority Priority, vars map[string]string) error {
|
||||
if conn == nil {
|
||||
return journalError("could not connect to journald socket")
|
||||
}
|
||||
|
||||
data := new(bytes.Buffer)
|
||||
appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
|
||||
appendVariable(data, "MESSAGE", message)
|
||||
for k, v := range vars {
|
||||
appendVariable(data, k, v)
|
||||
}
|
||||
|
||||
_, err := io.Copy(conn, data)
|
||||
if err != nil && isSocketSpaceError(err) {
|
||||
file, err := tempFd()
|
||||
if err != nil {
|
||||
return journalError(err.Error())
|
||||
}
|
||||
_, err = io.Copy(file, data)
|
||||
if err != nil {
|
||||
return journalError(err.Error())
|
||||
}
|
||||
|
||||
rights := syscall.UnixRights(int(file.Fd()))
|
||||
|
||||
/* this connection should always be a UnixConn, but better safe than sorry */
|
||||
unixConn, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return journalError("can't send file through non-Unix connection")
|
||||
}
|
||||
unixConn.WriteMsgUnix([]byte{}, rights, nil)
|
||||
} else if err != nil {
|
||||
return journalError(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendVariable(w io.Writer, name, value string) {
|
||||
if !validVarName(name) {
|
||||
journalError("variable name contains invalid character, ignoring")
|
||||
}
|
||||
if strings.ContainsRune(value, '\n') {
|
||||
/* When the value contains a newline, we write:
|
||||
* - the variable name, followed by a newline
|
||||
* - the size (in 64bit little endian format)
|
||||
* - the data, followed by a newline
|
||||
*/
|
||||
fmt.Fprintln(w, name)
|
||||
binary.Write(w, binary.LittleEndian, uint64(len(value)))
|
||||
fmt.Fprintln(w, value)
|
||||
} else {
|
||||
/* just write the variable and value all on one line */
|
||||
fmt.Fprintln(w, "%s=%s", name, value)
|
||||
}
|
||||
}
|
||||
|
||||
func validVarName(name string) bool {
|
||||
/* The variable name must be in uppercase and consist only of characters,
|
||||
* numbers and underscores, and may not begin with an underscore. (from the docs)
|
||||
*/
|
||||
|
||||
valid := name[0] != '_'
|
||||
for _, c := range name {
|
||||
valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
func isSocketSpaceError(err error) bool {
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
sysErr, ok := opErr.Err.(syscall.Errno)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
|
||||
}
|
||||
|
||||
func tempFd() (*os.File, error) {
|
||||
file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syscall.Unlink(file.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func journalError(s string) error {
|
||||
s = "journal error: " + s
|
||||
fmt.Fprintln(os.Stderr, s)
|
||||
return errors.New(s)
|
||||
}
|
||||
3
third_party/github.com/coreos/go-systemd/test
vendored
Executable file
3
third_party/github.com/coreos/go-systemd/test
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
go test -v ./...
|
||||
25
third_party/github.com/guelfey/go.dbus/LICENSE
vendored
Normal file
25
third_party/github.com/guelfey/go.dbus/LICENSE
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
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
|
||||
HOLDER 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.
|
||||
36
third_party/github.com/guelfey/go.dbus/README.markdown
vendored
Normal file
36
third_party/github.com/guelfey/go.dbus/README.markdown
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
go.dbus
|
||||
-------
|
||||
|
||||
go.dbus is a simple library that implements native Go client bindings for the
|
||||
D-Bus message bus system.
|
||||
|
||||
### Features
|
||||
|
||||
* Complete native implementation of the D-Bus message protocol
|
||||
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
|
||||
* Subpackages that help with the introspection / property interfaces
|
||||
|
||||
### Installation
|
||||
|
||||
This packages requires Go 1.1. If you installed it and set up your GOPATH, just run:
|
||||
|
||||
```
|
||||
go get github.com/guelfey/go.dbus
|
||||
```
|
||||
|
||||
If you want to use the subpackages, you can install them the same way.
|
||||
|
||||
### Usage
|
||||
|
||||
The complete package documentation and some simple examples are available at
|
||||
[godoc.org](http://godoc.org/github.com/guelfey/go.dbus). Also, the
|
||||
[_examples](https://github.com/guelfey/go.dbus/tree/master/_examples) directory
|
||||
gives a short overview over the basic usage.
|
||||
|
||||
Please note that the API is considered unstable for now and may change without
|
||||
further notice.
|
||||
|
||||
### License
|
||||
|
||||
go.dbus is available under the Simplified BSD License; see LICENSE for the full
|
||||
text.
|
||||
30
third_party/github.com/guelfey/go.dbus/_examples/eavesdrop.go
vendored
Normal file
30
third_party/github.com/guelfey/go.dbus/_examples/eavesdrop.go
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, v := range []string{"method_call", "method_return", "error", "signal"} {
|
||||
call := conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"eavesdrop='true',type='"+v+"'")
|
||||
if call.Err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to add match:", call.Err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
c := make(chan *dbus.Message, 10)
|
||||
conn.Eavesdrop(c)
|
||||
fmt.Println("Listening for everything")
|
||||
for v := range c {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
21
third_party/github.com/guelfey/go.dbus/_examples/introspect.go
vendored
Normal file
21
third_party/github.com/guelfey/go.dbus/_examples/introspect.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus/introspect"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
node, err := introspect.Call(conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data, _ := json.MarshalIndent(node, "", " ")
|
||||
os.Stdout.Write(data)
|
||||
}
|
||||
27
third_party/github.com/guelfey/go.dbus/_examples/list-names.go
vendored
Normal file
27
third_party/github.com/guelfey/go.dbus/_examples/list-names.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var s []string
|
||||
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&s)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to get list of owned names:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Currently owned names on the session bus:")
|
||||
for _, v := range s {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
17
third_party/github.com/guelfey/go.dbus/_examples/notification.go
vendored
Normal file
17
third_party/github.com/guelfey/go.dbus/_examples/notification.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import "github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
|
||||
call := obj.Call("org.freedesktop.Notifications.Notify", 0, "", uint32(0),
|
||||
"", "Test", "This is a test of the DBus bindings for go.", []string{},
|
||||
map[string]dbus.Variant{}, int32(5000))
|
||||
if call.Err != nil {
|
||||
panic(call.Err)
|
||||
}
|
||||
}
|
||||
68
third_party/github.com/guelfey/go.dbus/_examples/prop.go
vendored
Normal file
68
third_party/github.com/guelfey/go.dbus/_examples/prop.go
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus/introspect"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus/prop"
|
||||
"os"
|
||||
)
|
||||
|
||||
type foo string
|
||||
|
||||
func (f foo) Foo() (string, *dbus.Error) {
|
||||
fmt.Println(f)
|
||||
return string(f), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
reply, err := conn.RequestName("com.github.guelfey.Demo",
|
||||
dbus.NameFlagDoNotQueue)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if reply != dbus.RequestNameReplyPrimaryOwner {
|
||||
fmt.Fprintln(os.Stderr, "name already taken")
|
||||
os.Exit(1)
|
||||
}
|
||||
propsSpec := map[string]map[string]*prop.Prop{
|
||||
"com.github.guelfey.Demo": {
|
||||
"SomeInt": {
|
||||
int32(0),
|
||||
true,
|
||||
prop.EmitTrue,
|
||||
func(c *prop.Change) *dbus.Error {
|
||||
fmt.Println(c.Name, "changed to", c.Value)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
f := foo("Bar")
|
||||
conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo")
|
||||
props := prop.New(conn, "/com/github/guelfey/Demo", propsSpec)
|
||||
n := &introspect.Node{
|
||||
Name: "/com/github/guelfey/Demo",
|
||||
Interfaces: []introspect.Interface{
|
||||
introspect.IntrospectData,
|
||||
prop.IntrospectData,
|
||||
{
|
||||
Name: "com.github.guelfey.Demo",
|
||||
Methods: introspect.Methods(f),
|
||||
Properties: props.Introspection("com.github.guelfey.Demo"),
|
||||
},
|
||||
},
|
||||
}
|
||||
conn.Export(introspect.NewIntrospectable(n), "/com/github/guelfey/Demo",
|
||||
"org.freedesktop.DBus.Introspectable")
|
||||
fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...")
|
||||
|
||||
c := make(chan *dbus.Signal)
|
||||
conn.Signal(c)
|
||||
for _ = range c {
|
||||
}
|
||||
}
|
||||
45
third_party/github.com/guelfey/go.dbus/_examples/server.go
vendored
Normal file
45
third_party/github.com/guelfey/go.dbus/_examples/server.go
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus/introspect"
|
||||
"os"
|
||||
)
|
||||
|
||||
const intro = `
|
||||
<node>
|
||||
<interface name="com.github.guelfey.Demo">
|
||||
<method name="Foo">
|
||||
<arg direction="out" type="s"/>
|
||||
</method>
|
||||
</interface>` + introspect.IntrospectDataString + `</node> `
|
||||
|
||||
type foo string
|
||||
|
||||
func (f foo) Foo() (string, *dbus.Error) {
|
||||
fmt.Println(f)
|
||||
return string(f), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
reply, err := conn.RequestName("com.github.guelfey.Demo",
|
||||
dbus.NameFlagDoNotQueue)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if reply != dbus.RequestNameReplyPrimaryOwner {
|
||||
fmt.Fprintln(os.Stderr, "name already taken")
|
||||
os.Exit(1)
|
||||
}
|
||||
f := foo("Bar!")
|
||||
conn.Export(f, "/com/github/guelfey/Demo", "com.github.guelfey.Demo")
|
||||
conn.Export(introspect.Introspectable(intro), "/com/github/guelfey/Demo",
|
||||
"org.freedesktop.DBus.Introspectable")
|
||||
fmt.Println("Listening on com.github.guelfey.Demo / /com/github/guelfey/Demo ...")
|
||||
select {}
|
||||
}
|
||||
24
third_party/github.com/guelfey/go.dbus/_examples/signal.go
vendored
Normal file
24
third_party/github.com/guelfey/go.dbus/_examples/signal.go
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to connect to session bus:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',sender='org.freedesktop.DBus'")
|
||||
|
||||
c := make(chan *dbus.Signal, 10)
|
||||
conn.Signal(c)
|
||||
for v := range c {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
255
third_party/github.com/guelfey/go.dbus/auth.go
vendored
Normal file
255
third_party/github.com/guelfey/go.dbus/auth.go
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
// AuthStatus represents the Status of an authentication mechanism.
|
||||
type AuthStatus byte
|
||||
|
||||
const (
|
||||
// AuthOk signals that authentication is finished; the next command
|
||||
// from the server should be an OK.
|
||||
AuthOk AuthStatus = iota
|
||||
|
||||
// AuthContinue signals that additional data is needed; the next command
|
||||
// from the server should be a DATA.
|
||||
AuthContinue
|
||||
|
||||
// AuthError signals an error; the server sent invalid data or some
|
||||
// other unexpected thing happened and the current authentication
|
||||
// process should be aborted.
|
||||
AuthError
|
||||
)
|
||||
|
||||
type authState byte
|
||||
|
||||
const (
|
||||
waitingForData authState = iota
|
||||
waitingForOk
|
||||
waitingForReject
|
||||
)
|
||||
|
||||
// Auth defines the behaviour of an authentication mechanism.
|
||||
type Auth interface {
|
||||
// Return the name of the mechnism, the argument to the first AUTH command
|
||||
// and the next status.
|
||||
FirstData() (name, resp []byte, status AuthStatus)
|
||||
|
||||
// Process the given DATA command, and return the argument to the DATA
|
||||
// command and the next status. If len(resp) == 0, no DATA command is sent.
|
||||
HandleData(data []byte) (resp []byte, status AuthStatus)
|
||||
}
|
||||
|
||||
// Auth authenticates the connection, trying the given list of authentication
|
||||
// mechanisms (in that order). If nil is passed, the EXTERNAL and
|
||||
// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private
|
||||
// connections, this method must be called before sending any messages to the
|
||||
// bus. Auth must not be called on shared connections.
|
||||
func (conn *Conn) Auth(methods []Auth) error {
|
||||
if methods == nil {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
methods = []Auth{AuthExternal(u.Username), AuthCookieSha1(u.Username, u.HomeDir)}
|
||||
}
|
||||
in := bufio.NewReader(conn.transport)
|
||||
err := conn.transport.SendNullByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = authWriteLine(conn.transport, []byte("AUTH"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) {
|
||||
return errors.New("dbus: authentication protocol error")
|
||||
}
|
||||
s = s[1:]
|
||||
for _, v := range s {
|
||||
for _, m := range methods {
|
||||
if name, data, status := m.FirstData(); bytes.Equal(v, name) {
|
||||
var ok bool
|
||||
err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch status {
|
||||
case AuthOk:
|
||||
err, ok = conn.tryAuth(m, waitingForOk, in)
|
||||
case AuthContinue:
|
||||
err, ok = conn.tryAuth(m, waitingForData, in)
|
||||
default:
|
||||
panic("dbus: invalid authentication status")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if conn.transport.SupportsUnixFDs() {
|
||||
err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")):
|
||||
conn.EnableUnixFDs()
|
||||
conn.unixFD = true
|
||||
case bytes.Equal(line[0], []byte("ERROR")):
|
||||
default:
|
||||
return errors.New("dbus: authentication protocol error")
|
||||
}
|
||||
}
|
||||
err = authWriteLine(conn.transport, []byte("BEGIN"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go conn.inWorker()
|
||||
go conn.outWorker()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("dbus: authentication failed")
|
||||
}
|
||||
|
||||
// tryAuth tries to authenticate with m as the mechanism, using state as the
|
||||
// initial authState and in for reading input. It returns (nil, true) on
|
||||
// success, (nil, false) on a REJECTED and (someErr, false) if some other
|
||||
// error occured.
|
||||
func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) {
|
||||
for {
|
||||
s, err := authReadLine(in)
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
switch {
|
||||
case state == waitingForData && string(s[0]) == "DATA":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
continue
|
||||
}
|
||||
data, status := m.HandleData(s[1])
|
||||
switch status {
|
||||
case AuthOk, AuthContinue:
|
||||
if len(data) != 0 {
|
||||
err = authWriteLine(conn.transport, []byte("DATA"), data)
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
}
|
||||
if status == AuthOk {
|
||||
state = waitingForOk
|
||||
}
|
||||
case AuthError:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
}
|
||||
case state == waitingForData && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForData && string(s[0]) == "ERROR":
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
case state == waitingForData && string(s[0]) == "OK":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
}
|
||||
conn.uuid = string(s[1])
|
||||
return nil, true
|
||||
case state == waitingForData:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
case state == waitingForOk && string(s[0]) == "OK":
|
||||
if len(s) != 2 {
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
}
|
||||
conn.uuid = string(s[1])
|
||||
return nil, true
|
||||
case state == waitingForOk && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForOk && (string(s[0]) == "DATA" ||
|
||||
string(s[0]) == "ERROR"):
|
||||
|
||||
err = authWriteLine(conn.transport, []byte("CANCEL"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
state = waitingForReject
|
||||
case state == waitingForOk:
|
||||
err = authWriteLine(conn.transport, []byte("ERROR"))
|
||||
if err != nil {
|
||||
return err, false
|
||||
}
|
||||
case state == waitingForReject && string(s[0]) == "REJECTED":
|
||||
return nil, false
|
||||
case state == waitingForReject:
|
||||
return errors.New("dbus: authentication protocol error"), false
|
||||
default:
|
||||
panic("dbus: invalid auth state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// authReadLine reads a line and separates it into its fields.
|
||||
func authReadLine(in *bufio.Reader) ([][]byte, error) {
|
||||
data, err := in.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = bytes.TrimSuffix(data, []byte("\r\n"))
|
||||
return bytes.Split(data, []byte{' '}), nil
|
||||
}
|
||||
|
||||
// authWriteLine writes the given line in the authentication protocol format
|
||||
// (elements of data separated by a " " and terminated by "\r\n").
|
||||
func authWriteLine(out io.Writer, data ...[]byte) error {
|
||||
buf := make([]byte, 0)
|
||||
for i, v := range data {
|
||||
buf = append(buf, v...)
|
||||
if i != len(data)-1 {
|
||||
buf = append(buf, ' ')
|
||||
}
|
||||
}
|
||||
buf = append(buf, '\r')
|
||||
buf = append(buf, '\n')
|
||||
n, err := out.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(buf) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
third_party/github.com/guelfey/go.dbus/auth_external.go
vendored
Normal file
26
third_party/github.com/guelfey/go.dbus/auth_external.go
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// AuthExternal returns an Auth that authenticates as the given user with the
|
||||
// EXTERNAL mechanism.
|
||||
func AuthExternal(user string) Auth {
|
||||
return authExternal{user}
|
||||
}
|
||||
|
||||
// AuthExternal implements the EXTERNAL authentication mechanism.
|
||||
type authExternal struct {
|
||||
user string
|
||||
}
|
||||
|
||||
func (a authExternal) FirstData() ([]byte, []byte, AuthStatus) {
|
||||
b := make([]byte, 2*len(a.user))
|
||||
hex.Encode(b, []byte(a.user))
|
||||
return []byte("EXTERNAL"), b, AuthOk
|
||||
}
|
||||
|
||||
func (a authExternal) HandleData(b []byte) ([]byte, AuthStatus) {
|
||||
return nil, AuthError
|
||||
}
|
||||
102
third_party/github.com/guelfey/go.dbus/auth_sha1.go
vendored
Normal file
102
third_party/github.com/guelfey/go.dbus/auth_sha1.go
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
)
|
||||
|
||||
// AuthCookieSha1 returns an Auth that authenticates as the given user with the
|
||||
// DBUS_COOKIE_SHA1 mechanism. The home parameter should specify the home
|
||||
// directory of the user.
|
||||
func AuthCookieSha1(user, home string) Auth {
|
||||
return authCookieSha1{user, home}
|
||||
}
|
||||
|
||||
type authCookieSha1 struct {
|
||||
user, home string
|
||||
}
|
||||
|
||||
func (a authCookieSha1) FirstData() ([]byte, []byte, AuthStatus) {
|
||||
b := make([]byte, 2*len(a.user))
|
||||
hex.Encode(b, []byte(a.user))
|
||||
return []byte("DBUS_COOKIE_SHA1"), b, AuthContinue
|
||||
}
|
||||
|
||||
func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) {
|
||||
challenge := make([]byte, len(data)/2)
|
||||
_, err := hex.Decode(challenge, data)
|
||||
if err != nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
b := bytes.Split(challenge, []byte{' '})
|
||||
if len(b) != 3 {
|
||||
return nil, AuthError
|
||||
}
|
||||
context := b[0]
|
||||
id := b[1]
|
||||
svchallenge := b[2]
|
||||
cookie := a.getCookie(context, id)
|
||||
if cookie == nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
clchallenge := a.generateChallenge()
|
||||
if clchallenge == nil {
|
||||
return nil, AuthError
|
||||
}
|
||||
hash := sha1.New()
|
||||
hash.Write(bytes.Join([][]byte{svchallenge, clchallenge, cookie}, []byte{':'}))
|
||||
hexhash := make([]byte, 2*hash.Size())
|
||||
hex.Encode(hexhash, hash.Sum(nil))
|
||||
data = append(clchallenge, ' ')
|
||||
data = append(data, hexhash...)
|
||||
resp := make([]byte, 2*len(data))
|
||||
hex.Encode(resp, data)
|
||||
return resp, AuthOk
|
||||
}
|
||||
|
||||
// getCookie searches for the cookie identified by id in context and returns
|
||||
// the cookie content or nil. (Since HandleData can't return a specific error,
|
||||
// but only whether an error occured, this function also doesn't bother to
|
||||
// return an error.)
|
||||
func (a authCookieSha1) getCookie(context, id []byte) []byte {
|
||||
file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
rd := bufio.NewReader(file)
|
||||
for {
|
||||
line, err := rd.ReadBytes('\n')
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
line = line[:len(line)-1]
|
||||
b := bytes.Split(line, []byte{' '})
|
||||
if len(b) != 3 {
|
||||
return nil
|
||||
}
|
||||
if bytes.Equal(b[0], id) {
|
||||
return b[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateChallenge returns a random, hex-encoded challenge, or nil on error
|
||||
// (see above).
|
||||
func (a authCookieSha1) generateChallenge() []byte {
|
||||
b := make([]byte, 16)
|
||||
n, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if n != 16 {
|
||||
return nil
|
||||
}
|
||||
enc := make([]byte, 32)
|
||||
hex.Encode(enc, b)
|
||||
return enc
|
||||
}
|
||||
147
third_party/github.com/guelfey/go.dbus/call.go
vendored
Normal file
147
third_party/github.com/guelfey/go.dbus/call.go
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Call represents a pending or completed method call.
|
||||
type Call struct {
|
||||
Destination string
|
||||
Path ObjectPath
|
||||
Method string
|
||||
Args []interface{}
|
||||
|
||||
// Strobes when the call is complete.
|
||||
Done chan *Call
|
||||
|
||||
// After completion, the error status. If this is non-nil, it may be an
|
||||
// error message from the peer (with Error as its type) or some other error.
|
||||
Err error
|
||||
|
||||
// Holds the response once the call is done.
|
||||
Body []interface{}
|
||||
}
|
||||
|
||||
var errSignature = errors.New("dbus: mismatched signature")
|
||||
|
||||
// Store stores the body of the reply into the provided pointers. It returns
|
||||
// an error if the signatures of the body and retvalues don't match, or if
|
||||
// the error status is not nil.
|
||||
func (c *Call) Store(retvalues ...interface{}) error {
|
||||
if c.Err != nil {
|
||||
return c.Err
|
||||
}
|
||||
|
||||
return Store(c.Body, retvalues...)
|
||||
}
|
||||
|
||||
// Object represents a remote object on which methods can be invoked.
|
||||
type Object struct {
|
||||
conn *Conn
|
||||
dest string
|
||||
path ObjectPath
|
||||
}
|
||||
|
||||
// Call calls a method with (*Object).Go and waits for its reply.
|
||||
func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call {
|
||||
return <-o.Go(method, flags, make(chan *Call, 1), args...).Done
|
||||
}
|
||||
|
||||
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||
// object. The property name must be given in interface.member notation.
|
||||
func (o *Object) GetProperty(p string) (Variant, error) {
|
||||
idx := strings.LastIndex(p, ".")
|
||||
if idx == -1 || idx+1 == len(p) {
|
||||
return Variant{}, errors.New("dbus: invalid property " + p)
|
||||
}
|
||||
|
||||
iface := p[:idx]
|
||||
prop := p[idx+1:]
|
||||
|
||||
result := Variant{}
|
||||
err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result)
|
||||
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Go calls a method with the given arguments asynchronously. It returns a
|
||||
// Call structure representing this method call. The passed channel will
|
||||
// return the same value once the call is done. If ch is nil, a new channel
|
||||
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||
//
|
||||
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||
// is returned of which only the Err member is valid.
|
||||
//
|
||||
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||
// specifies the interface on which the method is called.
|
||||
func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call {
|
||||
iface := ""
|
||||
i := strings.LastIndex(method, ".")
|
||||
if i != -1 {
|
||||
iface = method[:i]
|
||||
}
|
||||
method = method[i+1:]
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodCall
|
||||
msg.serial = o.conn.getSerial()
|
||||
msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected)
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldPath] = MakeVariant(o.path)
|
||||
msg.Headers[FieldDestination] = MakeVariant(o.dest)
|
||||
msg.Headers[FieldMember] = MakeVariant(method)
|
||||
if iface != "" {
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
}
|
||||
msg.Body = args
|
||||
if len(args) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...))
|
||||
}
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
if ch == nil {
|
||||
ch = make(chan *Call, 10)
|
||||
} else if cap(ch) == 0 {
|
||||
panic("dbus: unbuffered channel passed to (*Object).Go")
|
||||
}
|
||||
call := &Call{
|
||||
Destination: o.dest,
|
||||
Path: o.path,
|
||||
Method: method,
|
||||
Args: args,
|
||||
Done: ch,
|
||||
}
|
||||
o.conn.callsLck.Lock()
|
||||
o.conn.calls[msg.serial] = call
|
||||
o.conn.callsLck.Unlock()
|
||||
o.conn.outLck.RLock()
|
||||
if o.conn.closed {
|
||||
call.Err = ErrClosed
|
||||
call.Done <- call
|
||||
} else {
|
||||
o.conn.out <- msg
|
||||
}
|
||||
o.conn.outLck.RUnlock()
|
||||
return call
|
||||
}
|
||||
o.conn.outLck.RLock()
|
||||
defer o.conn.outLck.RUnlock()
|
||||
if o.conn.closed {
|
||||
return &Call{Err: ErrClosed}
|
||||
}
|
||||
o.conn.out <- msg
|
||||
return &Call{Err: nil}
|
||||
}
|
||||
|
||||
// Destination returns the destination that calls on o are sent to.
|
||||
func (o *Object) Destination() string {
|
||||
return o.dest
|
||||
}
|
||||
|
||||
// Path returns the path that calls on o are sent to.
|
||||
func (o *Object) Path() ObjectPath {
|
||||
return o.path
|
||||
}
|
||||
597
third_party/github.com/guelfey/go.dbus/conn.go
vendored
Normal file
597
third_party/github.com/guelfey/go.dbus/conn.go
vendored
Normal file
@@ -0,0 +1,597 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||
|
||||
var (
|
||||
systemBus *Conn
|
||||
systemBusLck sync.Mutex
|
||||
sessionBus *Conn
|
||||
sessionBusLck sync.Mutex
|
||||
)
|
||||
|
||||
// ErrClosed is the error returned by calls on a closed connection.
|
||||
var ErrClosed = errors.New("dbus: connection closed by user")
|
||||
|
||||
// Conn represents a connection to a message bus (usually, the system or
|
||||
// session bus).
|
||||
//
|
||||
// Connections are either shared or private. Shared connections
|
||||
// are shared between calls to the functions that return them. As a result,
|
||||
// the methods Close, Auth and Hello must not be called on them.
|
||||
//
|
||||
// Multiple goroutines may invoke methods on a connection simultaneously.
|
||||
type Conn struct {
|
||||
transport
|
||||
|
||||
busObj *Object
|
||||
unixFD bool
|
||||
uuid string
|
||||
|
||||
names []string
|
||||
namesLck sync.RWMutex
|
||||
|
||||
serialLck sync.Mutex
|
||||
nextSerial uint32
|
||||
serialUsed map[uint32]bool
|
||||
|
||||
calls map[uint32]*Call
|
||||
callsLck sync.RWMutex
|
||||
|
||||
handlers map[ObjectPath]map[string]interface{}
|
||||
handlersLck sync.RWMutex
|
||||
|
||||
out chan *Message
|
||||
closed bool
|
||||
outLck sync.RWMutex
|
||||
|
||||
signals []chan<- *Signal
|
||||
signalsLck sync.Mutex
|
||||
|
||||
eavesdropped chan<- *Message
|
||||
eavesdroppedLck sync.Mutex
|
||||
}
|
||||
|
||||
// SessionBus returns a shared connection to the session bus, connecting to it
|
||||
// if not already done.
|
||||
func SessionBus() (conn *Conn, err error) {
|
||||
sessionBusLck.Lock()
|
||||
defer sessionBusLck.Unlock()
|
||||
if sessionBus != nil {
|
||||
return sessionBus, nil
|
||||
}
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
sessionBus = conn
|
||||
}
|
||||
}()
|
||||
conn, err = SessionBusPrivate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.Auth(nil); err != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
return
|
||||
}
|
||||
if err = conn.Hello(); err != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SessionBusPrivate returns a new private connection to the session bus.
|
||||
func SessionBusPrivate() (*Conn, error) {
|
||||
address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
|
||||
if address != "" && address != "autolaunch:" {
|
||||
return Dial(address)
|
||||
}
|
||||
|
||||
return sessionBusPlatform()
|
||||
}
|
||||
|
||||
// SystemBus returns a shared connection to the system bus, connecting to it if
|
||||
// not already done.
|
||||
func SystemBus() (conn *Conn, err error) {
|
||||
systemBusLck.Lock()
|
||||
defer systemBusLck.Unlock()
|
||||
if systemBus != nil {
|
||||
return systemBus, nil
|
||||
}
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
systemBus = conn
|
||||
}
|
||||
}()
|
||||
conn, err = SystemBusPrivate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.Auth(nil); err != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
return
|
||||
}
|
||||
if err = conn.Hello(); err != nil {
|
||||
conn.Close()
|
||||
conn = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SystemBusPrivate returns a new private connection to the system bus.
|
||||
func SystemBusPrivate() (*Conn, error) {
|
||||
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||
if address != "" {
|
||||
return Dial(address)
|
||||
}
|
||||
return Dial(defaultSystemBusAddress)
|
||||
}
|
||||
|
||||
// Dial establishes a new private connection to the message bus specified by address.
|
||||
func Dial(address string) (*Conn, error) {
|
||||
tr, err := getTransport(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newConn(tr)
|
||||
}
|
||||
|
||||
// NewConn creates a new private *Conn from an already established connection.
|
||||
func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
|
||||
return newConn(genericTransport{conn})
|
||||
}
|
||||
|
||||
// newConn creates a new *Conn from a transport.
|
||||
func newConn(tr transport) (*Conn, error) {
|
||||
conn := new(Conn)
|
||||
conn.transport = tr
|
||||
conn.calls = make(map[uint32]*Call)
|
||||
conn.out = make(chan *Message, 10)
|
||||
conn.handlers = make(map[ObjectPath]map[string]interface{})
|
||||
conn.nextSerial = 1
|
||||
conn.serialUsed = map[uint32]bool{0: true}
|
||||
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// BusObject returns the object owned by the bus daemon which handles
|
||||
// administrative requests.
|
||||
func (conn *Conn) BusObject() *Object {
|
||||
return conn.busObj
|
||||
}
|
||||
|
||||
// Close closes the connection. Any blocked operations will return with errors
|
||||
// and the channels passed to Eavesdrop and Signal are closed. This method must
|
||||
// not be called on shared connections.
|
||||
func (conn *Conn) Close() error {
|
||||
conn.outLck.Lock()
|
||||
close(conn.out)
|
||||
conn.closed = true
|
||||
conn.outLck.Unlock()
|
||||
conn.signalsLck.Lock()
|
||||
for _, ch := range conn.signals {
|
||||
close(ch)
|
||||
}
|
||||
conn.signalsLck.Unlock()
|
||||
conn.eavesdroppedLck.Lock()
|
||||
if conn.eavesdropped != nil {
|
||||
close(conn.eavesdropped)
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
return conn.transport.Close()
|
||||
}
|
||||
|
||||
// Eavesdrop causes conn to send all incoming messages to the given channel
|
||||
// without further processing. Method replies, errors and signals will not be
|
||||
// sent to the appropiate channels and method calls will not be handled. If nil
|
||||
// is passed, the normal behaviour is restored.
|
||||
//
|
||||
// The caller has to make sure that ch is sufficiently buffered;
|
||||
// if a message arrives when a write to ch is not possible, the message is
|
||||
// discarded.
|
||||
func (conn *Conn) Eavesdrop(ch chan<- *Message) {
|
||||
conn.eavesdroppedLck.Lock()
|
||||
conn.eavesdropped = ch
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
}
|
||||
|
||||
// getSerial returns an unused serial.
|
||||
func (conn *Conn) getSerial() uint32 {
|
||||
conn.serialLck.Lock()
|
||||
defer conn.serialLck.Unlock()
|
||||
n := conn.nextSerial
|
||||
for conn.serialUsed[n] {
|
||||
n++
|
||||
}
|
||||
conn.serialUsed[n] = true
|
||||
conn.nextSerial = n + 1
|
||||
return n
|
||||
}
|
||||
|
||||
// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be
|
||||
// called after authentication, but before sending any other messages to the
|
||||
// bus. Hello must not be called for shared connections.
|
||||
func (conn *Conn) Hello() error {
|
||||
var s string
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.namesLck.Lock()
|
||||
conn.names = make([]string, 1)
|
||||
conn.names[0] = s
|
||||
conn.namesLck.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// inWorker runs in an own goroutine, reading incoming messages from the
|
||||
// transport and dispatching them appropiately.
|
||||
func (conn *Conn) inWorker() {
|
||||
for {
|
||||
msg, err := conn.ReadMessage()
|
||||
if err == nil {
|
||||
conn.eavesdroppedLck.Lock()
|
||||
if conn.eavesdropped != nil {
|
||||
select {
|
||||
case conn.eavesdropped <- msg:
|
||||
default:
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
continue
|
||||
}
|
||||
conn.eavesdroppedLck.Unlock()
|
||||
dest, _ := msg.Headers[FieldDestination].value.(string)
|
||||
found := false
|
||||
if dest == "" {
|
||||
found = true
|
||||
} else {
|
||||
conn.namesLck.RLock()
|
||||
if len(conn.names) == 0 {
|
||||
found = true
|
||||
}
|
||||
for _, v := range conn.names {
|
||||
if dest == v {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.namesLck.RUnlock()
|
||||
}
|
||||
if !found {
|
||||
// Eavesdropped a message, but no channel for it is registered.
|
||||
// Ignore it.
|
||||
continue
|
||||
}
|
||||
switch msg.Type {
|
||||
case TypeMethodReply, TypeError:
|
||||
serial := msg.Headers[FieldReplySerial].value.(uint32)
|
||||
conn.callsLck.Lock()
|
||||
if c, ok := conn.calls[serial]; ok {
|
||||
if msg.Type == TypeError {
|
||||
name, _ := msg.Headers[FieldErrorName].value.(string)
|
||||
c.Err = Error{name, msg.Body}
|
||||
} else {
|
||||
c.Body = msg.Body
|
||||
}
|
||||
c.Done <- c
|
||||
conn.serialLck.Lock()
|
||||
delete(conn.serialUsed, serial)
|
||||
conn.serialLck.Unlock()
|
||||
delete(conn.calls, serial)
|
||||
}
|
||||
conn.callsLck.Unlock()
|
||||
case TypeSignal:
|
||||
iface := msg.Headers[FieldInterface].value.(string)
|
||||
member := msg.Headers[FieldMember].value.(string)
|
||||
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||
// sender is optional for signals.
|
||||
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||
if iface == "org.freedesktop.DBus" && member == "NameLost" &&
|
||||
sender == "org.freedesktop.DBus" {
|
||||
|
||||
name, _ := msg.Body[0].(string)
|
||||
conn.namesLck.Lock()
|
||||
for i, v := range conn.names {
|
||||
if v == name {
|
||||
copy(conn.names[i:], conn.names[i+1:])
|
||||
conn.names = conn.names[:len(conn.names)-1]
|
||||
}
|
||||
}
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
signal := &Signal{
|
||||
Sender: sender,
|
||||
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||
Name: iface + "." + member,
|
||||
Body: msg.Body,
|
||||
}
|
||||
conn.signalsLck.Lock()
|
||||
for _, ch := range conn.signals {
|
||||
// don't block trying to send a signal
|
||||
select {
|
||||
case ch <- signal:
|
||||
default:
|
||||
}
|
||||
}
|
||||
conn.signalsLck.Unlock()
|
||||
case TypeMethodCall:
|
||||
go conn.handleCall(msg)
|
||||
}
|
||||
} else if _, ok := err.(InvalidMessageError); !ok {
|
||||
// Some read error occured (usually EOF); we can't really do
|
||||
// anything but to shut down all stuff and returns errors to all
|
||||
// pending replies.
|
||||
conn.Close()
|
||||
conn.callsLck.RLock()
|
||||
for _, v := range conn.calls {
|
||||
v.Err = err
|
||||
v.Done <- v
|
||||
}
|
||||
conn.callsLck.RUnlock()
|
||||
return
|
||||
}
|
||||
// invalid messages are ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Names returns the list of all names that are currently owned by this
|
||||
// connection. The slice is always at least one element long, the first element
|
||||
// being the unique name of the connection.
|
||||
func (conn *Conn) Names() []string {
|
||||
conn.namesLck.RLock()
|
||||
// copy the slice so it can't be modified
|
||||
s := make([]string, len(conn.names))
|
||||
copy(s, conn.names)
|
||||
conn.namesLck.RUnlock()
|
||||
return s
|
||||
}
|
||||
|
||||
// Object returns the object identified by the given destination name and path.
|
||||
func (conn *Conn) Object(dest string, path ObjectPath) *Object {
|
||||
return &Object{conn, dest, path}
|
||||
}
|
||||
|
||||
// outWorker runs in an own goroutine, encoding and sending messages that are
|
||||
// sent to conn.out.
|
||||
func (conn *Conn) outWorker() {
|
||||
for msg := range conn.out {
|
||||
err := conn.SendMessage(msg)
|
||||
conn.callsLck.RLock()
|
||||
if err != nil {
|
||||
if c := conn.calls[msg.serial]; c != nil {
|
||||
c.Err = err
|
||||
c.Done <- c
|
||||
}
|
||||
conn.serialLck.Lock()
|
||||
delete(conn.serialUsed, msg.serial)
|
||||
conn.serialLck.Unlock()
|
||||
} else if msg.Type != TypeMethodCall {
|
||||
conn.serialLck.Lock()
|
||||
delete(conn.serialUsed, msg.serial)
|
||||
conn.serialLck.Unlock()
|
||||
}
|
||||
conn.callsLck.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Send sends the given message to the message bus. You usually don't need to
|
||||
// use this; use the higher-level equivalents (Call / Go, Emit and Export)
|
||||
// instead. If msg is a method call and NoReplyExpected is not set, a non-nil
|
||||
// call is returned and the same value is sent to ch (which must be buffered)
|
||||
// once the call is complete. Otherwise, ch is ignored and a Call structure is
|
||||
// returned of which only the Err member is valid.
|
||||
func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
|
||||
var call *Call
|
||||
|
||||
msg.serial = conn.getSerial()
|
||||
if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 {
|
||||
if ch == nil {
|
||||
ch = make(chan *Call, 5)
|
||||
} else if cap(ch) == 0 {
|
||||
panic("dbus: unbuffered channel passed to (*Conn).Send")
|
||||
}
|
||||
call = new(Call)
|
||||
call.Destination, _ = msg.Headers[FieldDestination].value.(string)
|
||||
call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath)
|
||||
iface, _ := msg.Headers[FieldInterface].value.(string)
|
||||
member, _ := msg.Headers[FieldMember].value.(string)
|
||||
call.Method = iface + "." + member
|
||||
call.Args = msg.Body
|
||||
call.Done = ch
|
||||
conn.callsLck.Lock()
|
||||
conn.calls[msg.serial] = call
|
||||
conn.callsLck.Unlock()
|
||||
conn.outLck.RLock()
|
||||
if conn.closed {
|
||||
call.Err = ErrClosed
|
||||
call.Done <- call
|
||||
} else {
|
||||
conn.out <- msg
|
||||
}
|
||||
conn.outLck.RUnlock()
|
||||
} else {
|
||||
conn.outLck.RLock()
|
||||
if conn.closed {
|
||||
call = &Call{Err: ErrClosed}
|
||||
} else {
|
||||
conn.out <- msg
|
||||
call = &Call{Err: nil}
|
||||
}
|
||||
conn.outLck.RUnlock()
|
||||
}
|
||||
return call
|
||||
}
|
||||
|
||||
// sendError creates an error message corresponding to the parameters and sends
|
||||
// it to conn.out.
|
||||
func (conn *Conn) sendError(e Error, dest string, serial uint32) {
|
||||
msg := new(Message)
|
||||
msg.Type = TypeError
|
||||
msg.serial = conn.getSerial()
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||
msg.Headers[FieldErrorName] = MakeVariant(e.Name)
|
||||
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||
msg.Body = e.Body
|
||||
if len(e.Body) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...))
|
||||
}
|
||||
conn.outLck.RLock()
|
||||
if !conn.closed {
|
||||
conn.out <- msg
|
||||
}
|
||||
conn.outLck.RUnlock()
|
||||
}
|
||||
|
||||
// sendReply creates a method reply message corresponding to the parameters and
|
||||
// sends it to conn.out.
|
||||
func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||
msg := new(Message)
|
||||
msg.Type = TypeMethodReply
|
||||
msg.serial = conn.getSerial()
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldDestination] = MakeVariant(dest)
|
||||
msg.Headers[FieldReplySerial] = MakeVariant(serial)
|
||||
msg.Body = values
|
||||
if len(values) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||
}
|
||||
conn.outLck.RLock()
|
||||
if !conn.closed {
|
||||
conn.out <- msg
|
||||
}
|
||||
conn.outLck.RUnlock()
|
||||
}
|
||||
|
||||
// Signal registers the given channel to be passed all received signal messages.
|
||||
// The caller has to make sure that ch is sufficiently buffered; if a message
|
||||
// arrives when a write to c is not possible, it is discarded.
|
||||
//
|
||||
// Multiple of these channels can be registered at the same time. Passing a
|
||||
// channel that already is registered will remove it from the list of the
|
||||
// registered channels.
|
||||
//
|
||||
// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a
|
||||
// channel for eavesdropped messages, this channel receives all signals, and
|
||||
// none of the channels passed to Signal will receive any signals.
|
||||
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||
conn.signalsLck.Lock()
|
||||
conn.signals = append(conn.signals, ch)
|
||||
conn.signalsLck.Unlock()
|
||||
}
|
||||
|
||||
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
||||
// unix file descriptors. If this is false, method calls containing unix file
|
||||
// descriptors will return an error and emitted signals containing them will
|
||||
// not be sent.
|
||||
func (conn *Conn) SupportsUnixFDs() bool {
|
||||
return conn.unixFD
|
||||
}
|
||||
|
||||
// Error represents a D-Bus message of type Error.
|
||||
type Error struct {
|
||||
Name string
|
||||
Body []interface{}
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if len(e.Body) >= 1 {
|
||||
s, ok := e.Body[0].(string)
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
// Signal represents a D-Bus message of type Signal. The name member is given in
|
||||
// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost.
|
||||
type Signal struct {
|
||||
Sender string
|
||||
Path ObjectPath
|
||||
Name string
|
||||
Body []interface{}
|
||||
}
|
||||
|
||||
// transport is a D-Bus transport.
|
||||
type transport interface {
|
||||
// Read and Write raw data (for example, for the authentication protocol).
|
||||
io.ReadWriteCloser
|
||||
|
||||
// Send the initial null byte used for the EXTERNAL mechanism.
|
||||
SendNullByte() error
|
||||
|
||||
// Returns whether this transport supports passing Unix FDs.
|
||||
SupportsUnixFDs() bool
|
||||
|
||||
// Signal the transport that Unix FD passing is enabled for this connection.
|
||||
EnableUnixFDs()
|
||||
|
||||
// Read / send a message, handling things like Unix FDs.
|
||||
ReadMessage() (*Message, error)
|
||||
SendMessage(*Message) error
|
||||
}
|
||||
|
||||
func getTransport(address string) (transport, error) {
|
||||
var err error
|
||||
var t transport
|
||||
|
||||
m := map[string]func(string) (transport, error){
|
||||
"unix": newUnixTransport,
|
||||
}
|
||||
addresses := strings.Split(address, ";")
|
||||
for _, v := range addresses {
|
||||
i := strings.IndexRune(v, ':')
|
||||
if i == -1 {
|
||||
err = errors.New("dbus: invalid bus address (no transport)")
|
||||
continue
|
||||
}
|
||||
f := m[v[:i]]
|
||||
if f == nil {
|
||||
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||
}
|
||||
t, err = f(v[i+1:])
|
||||
if err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// dereferenceAll returns a slice that, assuming that vs is a slice of pointers
|
||||
// of arbitrary types, containes the values that are obtained from dereferencing
|
||||
// all elements in vs.
|
||||
func dereferenceAll(vs []interface{}) []interface{} {
|
||||
for i := range vs {
|
||||
v := reflect.ValueOf(vs[i])
|
||||
v = v.Elem()
|
||||
vs[i] = v.Interface()
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// getKey gets a key from a the list of keys. Returns "" on error / not found...
|
||||
func getKey(s, key string) string {
|
||||
i := strings.Index(s, key)
|
||||
if i == -1 {
|
||||
return ""
|
||||
}
|
||||
if i+len(key)+1 >= len(s) || s[i+len(key)] != '=' {
|
||||
return ""
|
||||
}
|
||||
j := strings.Index(s, ",")
|
||||
if j == -1 {
|
||||
j = len(s)
|
||||
}
|
||||
return s[i+len(key)+1 : j]
|
||||
}
|
||||
21
third_party/github.com/guelfey/go.dbus/conn_darwin.go
vendored
Normal file
21
third_party/github.com/guelfey/go.dbus/conn_darwin.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func sessionBusPlatform() (*Conn, error) {
|
||||
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||
b, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return nil, errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
|
||||
return Dial("unix:path=" + string(b[:len(b)-1]))
|
||||
}
|
||||
27
third_party/github.com/guelfey/go.dbus/conn_other.go
vendored
Normal file
27
third_party/github.com/guelfey/go.dbus/conn_other.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// +build !darwin
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func sessionBusPlatform() (*Conn, error) {
|
||||
cmd := exec.Command("dbus-launch")
|
||||
b, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := bytes.IndexByte(b, '=')
|
||||
j := bytes.IndexByte(b, '\n')
|
||||
|
||||
if i == -1 || j == -1 {
|
||||
return nil, errors.New("dbus: couldn't determine address of session bus")
|
||||
}
|
||||
|
||||
return Dial(string(b[i+1 : j]))
|
||||
}
|
||||
199
third_party/github.com/guelfey/go.dbus/conn_test.go
vendored
Normal file
199
third_party/github.com/guelfey/go.dbus/conn_test.go
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
package dbus
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSessionBus(t *testing.T) {
|
||||
_, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBus(t *testing.T) {
|
||||
_, err := SystemBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ch := make(chan *Call, 1)
|
||||
msg := &Message{
|
||||
Type: TypeMethodCall,
|
||||
Flags: 0,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant(bus.Names()[0]),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.DBus.Peer"),
|
||||
FieldMember: MakeVariant("Ping"),
|
||||
},
|
||||
}
|
||||
call := bus.Send(msg, ch)
|
||||
<-ch
|
||||
if call.Err != nil {
|
||||
t.Error(call.Err)
|
||||
}
|
||||
}
|
||||
|
||||
type server struct{}
|
||||
|
||||
func (server) Double(i int64) (int64, *Error) {
|
||||
return 2 * i, nil
|
||||
}
|
||||
|
||||
func BenchmarkCall(b *testing.B) {
|
||||
b.StopTimer()
|
||||
var s string
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
name := bus.Names()[0]
|
||||
obj := bus.BusObject()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if s != name {
|
||||
b.Errorf("got %s, wanted %s", s, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCallAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
name := bus.Names()[0]
|
||||
obj := bus.BusObject()
|
||||
c := make(chan *Call, 50)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := <-c
|
||||
if v.Err != nil {
|
||||
b.Error(v.Err)
|
||||
}
|
||||
s := v.Body[0].(string)
|
||||
if s != name {
|
||||
b.Errorf("got %s, wanted %s", s, name)
|
||||
}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func BenchmarkServe(b *testing.B) {
|
||||
b.StopTimer()
|
||||
srv, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
cli, err := SessionBusPrivate()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Auth(nil); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Hello(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
benchmarkServe(b, srv, cli)
|
||||
}
|
||||
|
||||
func BenchmarkServeAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
srv, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
cli, err := SessionBusPrivate()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Auth(nil); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err = cli.Hello(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
benchmarkServeAsync(b, srv, cli)
|
||||
}
|
||||
|
||||
func BenchmarkServeSameConn(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
benchmarkServe(b, bus, bus)
|
||||
}
|
||||
|
||||
func BenchmarkServeSameConnAsync(b *testing.B) {
|
||||
b.StopTimer()
|
||||
bus, err := SessionBus()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
benchmarkServeAsync(b, bus, bus)
|
||||
}
|
||||
|
||||
func benchmarkServe(b *testing.B, srv, cli *Conn) {
|
||||
var r int64
|
||||
var err error
|
||||
dest := srv.Names()[0]
|
||||
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if r != 2*int64(i) {
|
||||
b.Errorf("got %d, wanted %d", r, 2*int64(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
|
||||
dest := srv.Names()[0]
|
||||
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
|
||||
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
|
||||
c := make(chan *Call, 50)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := <-c
|
||||
if v.Err != nil {
|
||||
b.Fatal(v.Err)
|
||||
}
|
||||
i, r := v.Args[0].(int64), v.Body[0].(int64)
|
||||
if 2*i != r {
|
||||
b.Errorf("got %d, wanted %d", r, 2*i)
|
||||
}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
|
||||
}
|
||||
<-done
|
||||
}
|
||||
258
third_party/github.com/guelfey/go.dbus/dbus.go
vendored
Normal file
258
third_party/github.com/guelfey/go.dbus/dbus.go
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
byteType = reflect.TypeOf(byte(0))
|
||||
boolType = reflect.TypeOf(false)
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
int16Type = reflect.TypeOf(int16(0))
|
||||
uint16Type = reflect.TypeOf(uint16(0))
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
stringType = reflect.TypeOf("")
|
||||
signatureType = reflect.TypeOf(Signature{""})
|
||||
objectPathType = reflect.TypeOf(ObjectPath(""))
|
||||
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
||||
interfacesType = reflect.TypeOf([]interface{}{})
|
||||
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||
)
|
||||
|
||||
// An InvalidTypeError signals that a value which cannot be represented in the
|
||||
// D-Bus wire format was passed to a function.
|
||||
type InvalidTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e InvalidTypeError) Error() string {
|
||||
return "dbus: invalid type " + e.Type.String()
|
||||
}
|
||||
|
||||
// Store copies the values contained in src to dest, which must be a slice of
|
||||
// pointers. It converts slices of interfaces from src to corresponding structs
|
||||
// in dest. An error is returned if the lengths of src and dest or the types of
|
||||
// their elements don't match.
|
||||
func Store(src []interface{}, dest ...interface{}) error {
|
||||
if len(src) != len(dest) {
|
||||
return errors.New("dbus.Store: length mismatch")
|
||||
}
|
||||
|
||||
for i := range src {
|
||||
if err := store(src[i], dest[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func store(src, dest interface{}) error {
|
||||
if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) {
|
||||
reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
|
||||
return nil
|
||||
} else if hasStruct(dest) {
|
||||
rv := reflect.ValueOf(dest).Elem()
|
||||
switch rv.Kind() {
|
||||
case reflect.Struct:
|
||||
vs, ok := src.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
t := rv.Type()
|
||||
ndest := make([]interface{}, 0, rv.NumField())
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||
ndest = append(ndest, rv.Field(i).Addr().Interface())
|
||||
}
|
||||
}
|
||||
if len(vs) != len(ndest) {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
err := Store(vs, ndest...)
|
||||
if err != nil {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
case reflect.Slice:
|
||||
sv := reflect.ValueOf(src)
|
||||
if sv.Kind() != reflect.Slice {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len()))
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
sv := reflect.ValueOf(src)
|
||||
if sv.Kind() != reflect.Map {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
keys := sv.MapKeys()
|
||||
rv.Set(reflect.MakeMap(sv.Type()))
|
||||
for _, key := range keys {
|
||||
v := reflect.New(sv.Type().Elem())
|
||||
if err := store(v, sv.MapIndex(key).Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
rv.SetMapIndex(key, v.Elem())
|
||||
}
|
||||
default:
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("dbus.Store: type mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func hasStruct(v interface{}) bool {
|
||||
t := reflect.TypeOf(v)
|
||||
for {
|
||||
switch t.Kind() {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
case reflect.Slice, reflect.Ptr, reflect.Map:
|
||||
t = t.Elem()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An ObjectPath is an object path as defined by the D-Bus spec.
|
||||
type ObjectPath string
|
||||
|
||||
// IsValid returns whether the object path is valid.
|
||||
func (o ObjectPath) IsValid() bool {
|
||||
s := string(o)
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
if s[0] != '/' {
|
||||
return false
|
||||
}
|
||||
if s[len(s)-1] == '/' && len(s) != 1 {
|
||||
return false
|
||||
}
|
||||
// probably not used, but technically possible
|
||||
if s == "/" {
|
||||
return true
|
||||
}
|
||||
split := strings.Split(s[1:], "/")
|
||||
for _, v := range split {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range v {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// A UnixFD is a Unix file descriptor sent over the wire. See the package-level
|
||||
// documentation for more information about Unix file descriptor passsing.
|
||||
type UnixFD int32
|
||||
|
||||
// A UnixFDIndex is the representation of a Unix file descriptor in a message.
|
||||
type UnixFDIndex uint32
|
||||
|
||||
// alignment returns the alignment of values of type t.
|
||||
func alignment(t reflect.Type) int {
|
||||
switch t {
|
||||
case variantType:
|
||||
return 1
|
||||
case objectPathType:
|
||||
return 4
|
||||
case signatureType:
|
||||
return 1
|
||||
case interfacesType: // sometimes used for structs
|
||||
return 8
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8:
|
||||
return 1
|
||||
case reflect.Uint16, reflect.Int16:
|
||||
return 2
|
||||
case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
||||
return 4
|
||||
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
||||
return 8
|
||||
case reflect.Ptr:
|
||||
return alignment(t.Elem())
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// isKeyType returns whether t is a valid type for a D-Bus dict.
|
||||
func isKeyType(t reflect.Type) bool {
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
||||
reflect.String:
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isValidInterface returns whether s is a valid name for an interface.
|
||||
func isValidInterface(s string) bool {
|
||||
if len(s) == 0 || len(s) > 255 || s[0] == '.' {
|
||||
return false
|
||||
}
|
||||
elem := strings.Split(s, ".")
|
||||
if len(elem) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, v := range elem {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
if v[0] >= '0' && v[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
for _, c := range v {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isValidMember returns whether s is a valid name for a member.
|
||||
func isValidMember(s string) bool {
|
||||
if len(s) == 0 || len(s) > 255 {
|
||||
return false
|
||||
}
|
||||
i := strings.Index(s, ".")
|
||||
if i != -1 {
|
||||
return false
|
||||
}
|
||||
if s[0] >= '0' && s[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
if !isMemberChar(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isMemberChar(c rune) bool {
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') || c == '_'
|
||||
}
|
||||
228
third_party/github.com/guelfey/go.dbus/decoder.go
vendored
Normal file
228
third_party/github.com/guelfey/go.dbus/decoder.go
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
in io.Reader
|
||||
order binary.ByteOrder
|
||||
pos int
|
||||
}
|
||||
|
||||
// newDecoder returns a new decoder that reads values from in. The input is
|
||||
// expected to be in the given byte order.
|
||||
func newDecoder(in io.Reader, order binary.ByteOrder) *decoder {
|
||||
dec := new(decoder)
|
||||
dec.in = in
|
||||
dec.order = order
|
||||
return dec
|
||||
}
|
||||
|
||||
// align aligns the input to the given boundary and panics on error.
|
||||
func (dec *decoder) align(n int) {
|
||||
if dec.pos%n != 0 {
|
||||
newpos := (dec.pos + n - 1) & ^(n - 1)
|
||||
empty := make([]byte, newpos-dec.pos)
|
||||
if _, err := io.ReadFull(dec.in, empty); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos = newpos
|
||||
}
|
||||
}
|
||||
|
||||
// Calls binary.Read(dec.in, dec.order, v) and panics on read errors.
|
||||
func (dec *decoder) binread(v interface{}) {
|
||||
if err := binary.Read(dec.in, dec.order, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) {
|
||||
defer func() {
|
||||
var ok bool
|
||||
v := recover()
|
||||
if err, ok = v.(error); ok {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
err = FormatError("unexpected EOF")
|
||||
}
|
||||
}
|
||||
}()
|
||||
vs = make([]interface{}, 0)
|
||||
s := sig.str
|
||||
for s != "" {
|
||||
err, rem := validSingle(s, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := dec.decode(s[:len(s)-len(rem)], 0)
|
||||
vs = append(vs, v)
|
||||
s = rem
|
||||
}
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
func (dec *decoder) decode(s string, depth int) interface{} {
|
||||
dec.align(alignment(typeFor(s)))
|
||||
switch s[0] {
|
||||
case 'y':
|
||||
var b [1]byte
|
||||
if _, err := dec.in.Read(b[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos++
|
||||
return b[0]
|
||||
case 'b':
|
||||
i := dec.decode("u", depth).(uint32)
|
||||
switch {
|
||||
case i == 0:
|
||||
return false
|
||||
case i == 1:
|
||||
return true
|
||||
default:
|
||||
panic(FormatError("invalid value for boolean"))
|
||||
}
|
||||
case 'n':
|
||||
var i int16
|
||||
dec.binread(&i)
|
||||
dec.pos += 2
|
||||
return i
|
||||
case 'i':
|
||||
var i int32
|
||||
dec.binread(&i)
|
||||
dec.pos += 4
|
||||
return i
|
||||
case 'x':
|
||||
var i int64
|
||||
dec.binread(&i)
|
||||
dec.pos += 8
|
||||
return i
|
||||
case 'q':
|
||||
var i uint16
|
||||
dec.binread(&i)
|
||||
dec.pos += 2
|
||||
return i
|
||||
case 'u':
|
||||
var i uint32
|
||||
dec.binread(&i)
|
||||
dec.pos += 4
|
||||
return i
|
||||
case 't':
|
||||
var i uint64
|
||||
dec.binread(&i)
|
||||
dec.pos += 8
|
||||
return i
|
||||
case 'd':
|
||||
var f float64
|
||||
dec.binread(&f)
|
||||
dec.pos += 8
|
||||
return f
|
||||
case 's':
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
b := make([]byte, int(length)+1)
|
||||
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos += int(length) + 1
|
||||
return string(b[:len(b)-1])
|
||||
case 'o':
|
||||
return ObjectPath(dec.decode("s", depth).(string))
|
||||
case 'g':
|
||||
length := dec.decode("y", depth).(byte)
|
||||
b := make([]byte, int(length)+1)
|
||||
if _, err := io.ReadFull(dec.in, b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dec.pos += int(length) + 1
|
||||
sig, err := ParseSignature(string(b[:len(b)-1]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sig
|
||||
case 'v':
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
var variant Variant
|
||||
sig := dec.decode("g", depth).(Signature)
|
||||
if len(sig.str) == 0 {
|
||||
panic(FormatError("variant signature is empty"))
|
||||
}
|
||||
err, rem := validSingle(sig.str, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if rem != "" {
|
||||
panic(FormatError("variant signature has multiple types"))
|
||||
}
|
||||
variant.sig = sig
|
||||
variant.value = dec.decode(sig.str, depth+1)
|
||||
return variant
|
||||
case 'h':
|
||||
return UnixFDIndex(dec.decode("u", depth).(uint32))
|
||||
case 'a':
|
||||
if len(s) > 1 && s[1] == '{' {
|
||||
ksig := s[2:3]
|
||||
vsig := s[3 : len(s)-1]
|
||||
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
|
||||
if depth >= 63 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
// Even for empty maps, the correct padding must be included
|
||||
dec.align(8)
|
||||
spos := dec.pos
|
||||
for dec.pos < spos+int(length) {
|
||||
dec.align(8)
|
||||
if !isKeyType(v.Type().Key()) {
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
kv := dec.decode(ksig, depth+2)
|
||||
vv := dec.decode(vsig, depth+2)
|
||||
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||
}
|
||||
return v.Interface()
|
||||
}
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
length := dec.decode("u", depth).(uint32)
|
||||
v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length))
|
||||
// Even for empty arrays, the correct padding must be included
|
||||
dec.align(alignment(typeFor(s[1:])))
|
||||
spos := dec.pos
|
||||
for dec.pos < spos+int(length) {
|
||||
ev := dec.decode(s[1:], depth+1)
|
||||
v = reflect.Append(v, reflect.ValueOf(ev))
|
||||
}
|
||||
return v.Interface()
|
||||
case '(':
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
dec.align(8)
|
||||
v := make([]interface{}, 0)
|
||||
s = s[1 : len(s)-1]
|
||||
for s != "" {
|
||||
err, rem := validSingle(s, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ev := dec.decode(s[:len(s)-len(rem)], depth+1)
|
||||
v = append(v, ev)
|
||||
s = rem
|
||||
}
|
||||
return v
|
||||
default:
|
||||
panic(SignatureError{Sig: s})
|
||||
}
|
||||
}
|
||||
|
||||
// A FormatError is an error in the wire format.
|
||||
type FormatError string
|
||||
|
||||
func (e FormatError) Error() string {
|
||||
return "dbus: wire format error: " + string(e)
|
||||
}
|
||||
63
third_party/github.com/guelfey/go.dbus/doc.go
vendored
Normal file
63
third_party/github.com/guelfey/go.dbus/doc.go
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Package dbus implements bindings to the D-Bus message bus system.
|
||||
|
||||
To use the message bus API, you first need to connect to a bus (usually the
|
||||
session or system bus). The acquired connection then can be used to call methods
|
||||
on remote objects and emit or receive signals. Using the Export method, you can
|
||||
arrange D-Bus methods calls to be directly translated to method calls on a Go
|
||||
value.
|
||||
|
||||
Conversion Rules
|
||||
|
||||
For outgoing messages, Go types are automatically converted to the
|
||||
corresponding D-Bus types. The following types are directly encoded as their
|
||||
respective D-Bus equivalents:
|
||||
|
||||
Go type | D-Bus type
|
||||
------------+-----------
|
||||
byte | BYTE
|
||||
bool | BOOLEAN
|
||||
int16 | INT16
|
||||
uint16 | UINT16
|
||||
int32 | INT32
|
||||
uint32 | UINT32
|
||||
int64 | INT64
|
||||
uint64 | UINT64
|
||||
float64 | DOUBLE
|
||||
string | STRING
|
||||
ObjectPath | OBJECT_PATH
|
||||
Signature | SIGNATURE
|
||||
Variant | VARIANT
|
||||
UnixFDIndex | UNIX_FD
|
||||
|
||||
Slices and arrays encode as ARRAYs of their element type.
|
||||
|
||||
Maps encode as DICTs, provided that their key type can be used as a key for
|
||||
a DICT.
|
||||
|
||||
Structs other than Variant and Signature encode as a STRUCT containing their
|
||||
exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will
|
||||
be skipped.
|
||||
|
||||
Pointers encode as the value they're pointed to.
|
||||
|
||||
Trying to encode any other type or a slice, map or struct containing an
|
||||
unsupported type will result in an InvalidTypeError.
|
||||
|
||||
For incoming messages, the inverse of these rules are used, with the exception
|
||||
of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
|
||||
containing the struct fields in the correct order. The Store function can be
|
||||
used to convert such values to Go structs.
|
||||
|
||||
Unix FD passing
|
||||
|
||||
Handling Unix file descriptors deserves special mention. To use them, you should
|
||||
first check that they are supported on a connection by calling SupportsUnixFDs.
|
||||
If it returns true, all method of Connection will translate messages containing
|
||||
UnixFD's to messages that are accompanied by the given file descriptors with the
|
||||
UnixFD values being substituted by the correct indices. Similarily, the indices
|
||||
of incoming messages are automatically resolved. It shouldn't be necessary to use
|
||||
UnixFDIndex.
|
||||
|
||||
*/
|
||||
package dbus
|
||||
179
third_party/github.com/guelfey/go.dbus/encoder.go
vendored
Normal file
179
third_party/github.com/guelfey/go.dbus/encoder.go
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// An encoder encodes values to the D-Bus wire format.
|
||||
type encoder struct {
|
||||
out io.Writer
|
||||
order binary.ByteOrder
|
||||
pos int
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to out in the given byte order.
|
||||
func newEncoder(out io.Writer, order binary.ByteOrder) *encoder {
|
||||
enc := new(encoder)
|
||||
enc.out = out
|
||||
enc.order = order
|
||||
return enc
|
||||
}
|
||||
|
||||
// Aligns the next output to be on a multiple of n. Panics on write errors.
|
||||
func (enc *encoder) align(n int) {
|
||||
if enc.pos%n != 0 {
|
||||
newpos := (enc.pos + n - 1) & ^(n - 1)
|
||||
empty := make([]byte, newpos-enc.pos)
|
||||
if _, err := enc.out.Write(empty); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos = newpos
|
||||
}
|
||||
}
|
||||
|
||||
// Calls binary.Write(enc.out, enc.order, v) and panics on write errors.
|
||||
func (enc *encoder) binwrite(v interface{}) {
|
||||
if err := binary.Write(enc.out, enc.order, v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes the given values to the underyling reader. All written values
|
||||
// are aligned properly as required by the D-Bus spec.
|
||||
func (enc *encoder) Encode(vs ...interface{}) (err error) {
|
||||
defer func() {
|
||||
err, _ = recover().(error)
|
||||
}()
|
||||
for _, v := range vs {
|
||||
enc.encode(reflect.ValueOf(v), 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encode encodes the given value to the writer and panics on error. depth holds
|
||||
// the depth of the container nesting.
|
||||
func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||
enc.align(alignment(v.Type()))
|
||||
switch v.Kind() {
|
||||
case reflect.Uint8:
|
||||
var b [1]byte
|
||||
b[0] = byte(v.Uint())
|
||||
if _, err := enc.out.Write(b[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos++
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
enc.encode(reflect.ValueOf(uint32(1)), depth)
|
||||
} else {
|
||||
enc.encode(reflect.ValueOf(uint32(0)), depth)
|
||||
}
|
||||
case reflect.Int16:
|
||||
enc.binwrite(int16(v.Int()))
|
||||
enc.pos += 2
|
||||
case reflect.Uint16:
|
||||
enc.binwrite(uint16(v.Uint()))
|
||||
enc.pos += 2
|
||||
case reflect.Int32:
|
||||
enc.binwrite(int32(v.Int()))
|
||||
enc.pos += 4
|
||||
case reflect.Uint32:
|
||||
enc.binwrite(uint32(v.Uint()))
|
||||
enc.pos += 4
|
||||
case reflect.Int64:
|
||||
enc.binwrite(v.Int())
|
||||
enc.pos += 8
|
||||
case reflect.Uint64:
|
||||
enc.binwrite(v.Uint())
|
||||
enc.pos += 8
|
||||
case reflect.Float64:
|
||||
enc.binwrite(v.Float())
|
||||
enc.pos += 8
|
||||
case reflect.String:
|
||||
enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth)
|
||||
b := make([]byte, v.Len()+1)
|
||||
copy(b, v.String())
|
||||
b[len(b)-1] = 0
|
||||
n, err := enc.out.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += n
|
||||
case reflect.Ptr:
|
||||
enc.encode(v.Elem(), depth)
|
||||
case reflect.Slice, reflect.Array:
|
||||
if depth >= 64 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoder(&buf, enc.order)
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
bufenc.encode(v.Index(i), depth+1)
|
||||
}
|
||||
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||
length := buf.Len()
|
||||
enc.align(alignment(v.Type().Elem()))
|
||||
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += length
|
||||
case reflect.Struct:
|
||||
if depth >= 64 && v.Type() != signatureType {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
switch t := v.Type(); t {
|
||||
case signatureType:
|
||||
str := v.Field(0)
|
||||
enc.encode(reflect.ValueOf(byte(str.Len())), depth+1)
|
||||
b := make([]byte, str.Len()+1)
|
||||
copy(b, str.String())
|
||||
b[len(b)-1] = 0
|
||||
n, err := enc.out.Write(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += n
|
||||
case variantType:
|
||||
variant := v.Interface().(Variant)
|
||||
enc.encode(reflect.ValueOf(variant.sig), depth+1)
|
||||
enc.encode(reflect.ValueOf(variant.value), depth+1)
|
||||
default:
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||
enc.encode(v.Field(i), depth+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
// Maps are arrays of structures, so they actually increase the depth by
|
||||
// 2.
|
||||
if depth >= 63 {
|
||||
panic(FormatError("input exceeds container depth limit"))
|
||||
}
|
||||
if !isKeyType(v.Type().Key()) {
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
keys := v.MapKeys()
|
||||
var buf bytes.Buffer
|
||||
bufenc := newEncoder(&buf, enc.order)
|
||||
for _, k := range keys {
|
||||
bufenc.align(8)
|
||||
bufenc.encode(k, depth+2)
|
||||
bufenc.encode(v.MapIndex(k), depth+2)
|
||||
}
|
||||
enc.encode(reflect.ValueOf(uint32(buf.Len())), depth)
|
||||
length := buf.Len()
|
||||
enc.align(8)
|
||||
if _, err := buf.WriteTo(enc.out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
enc.pos += length
|
||||
default:
|
||||
panic(InvalidTypeError{v.Type()})
|
||||
}
|
||||
}
|
||||
50
third_party/github.com/guelfey/go.dbus/examples_test.go
vendored
Normal file
50
third_party/github.com/guelfey/go.dbus/examples_test.go
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package dbus
|
||||
|
||||
import "fmt"
|
||||
|
||||
func ExampleConn_Emit() {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF))
|
||||
}
|
||||
|
||||
func ExampleObject_Call() {
|
||||
var list []string
|
||||
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range list {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleObject_Go() {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ch := make(chan *Call, 10)
|
||||
conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
|
||||
select {
|
||||
case call := <-ch:
|
||||
if call.Err != nil {
|
||||
panic(err)
|
||||
}
|
||||
list := call.Body[0].([]string)
|
||||
for _, v := range list {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// put some other cases here
|
||||
}
|
||||
}
|
||||
300
third_party/github.com/guelfey/go.dbus/export.go
vendored
Normal file
300
third_party/github.com/guelfey/go.dbus/export.go
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
errmsgInvalidArg = Error{
|
||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||
[]interface{}{"Invalid type / number of args"},
|
||||
}
|
||||
errmsgNoObject = Error{
|
||||
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||
[]interface{}{"No such object"},
|
||||
}
|
||||
errmsgUnknownMethod = Error{
|
||||
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||
[]interface{}{"Unknown / invalid method"},
|
||||
}
|
||||
)
|
||||
|
||||
// Sender is a type which can be used in exported methods to receive the message
|
||||
// sender.
|
||||
type Sender string
|
||||
|
||||
func exportedMethod(v interface{}, name string) reflect.Value {
|
||||
if v == nil {
|
||||
return reflect.Value{}
|
||||
}
|
||||
m := reflect.ValueOf(v).MethodByName(name)
|
||||
if !m.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
t := m.Type()
|
||||
if t.NumOut() == 0 ||
|
||||
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
|
||||
|
||||
return reflect.Value{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||
// pre-implemented ones and searches for a corresponding handler if not).
|
||||
func (conn *Conn) handleCall(msg *Message) {
|
||||
name := msg.Headers[FieldMember].value.(string)
|
||||
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||
ifaceName, hasIface := msg.Headers[FieldInterface].value.(string)
|
||||
sender := msg.Headers[FieldSender].value.(string)
|
||||
serial := msg.serial
|
||||
if ifaceName == "org.freedesktop.DBus.Peer" {
|
||||
switch name {
|
||||
case "Ping":
|
||||
conn.sendReply(sender, serial)
|
||||
case "GetMachineId":
|
||||
conn.sendReply(sender, serial, conn.uuid)
|
||||
default:
|
||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(name) == 0 || unicode.IsLower([]rune(name)[0]) {
|
||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||
}
|
||||
var m reflect.Value
|
||||
if hasIface {
|
||||
conn.handlersLck.RLock()
|
||||
obj, ok := conn.handlers[path]
|
||||
if !ok {
|
||||
conn.sendError(errmsgNoObject, sender, serial)
|
||||
conn.handlersLck.RUnlock()
|
||||
return
|
||||
}
|
||||
iface := obj[ifaceName]
|
||||
conn.handlersLck.RUnlock()
|
||||
m = exportedMethod(iface, name)
|
||||
} else {
|
||||
conn.handlersLck.RLock()
|
||||
if _, ok := conn.handlers[path]; !ok {
|
||||
conn.sendError(errmsgNoObject, sender, serial)
|
||||
conn.handlersLck.RUnlock()
|
||||
return
|
||||
}
|
||||
for _, v := range conn.handlers[path] {
|
||||
m = exportedMethod(v, name)
|
||||
if m.IsValid() {
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.handlersLck.RUnlock()
|
||||
}
|
||||
if !m.IsValid() {
|
||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
||||
return
|
||||
}
|
||||
t := m.Type()
|
||||
vs := msg.Body
|
||||
pointers := make([]interface{}, t.NumIn())
|
||||
decode := make([]interface{}, 0, len(vs))
|
||||
for i := 0; i < t.NumIn(); i++ {
|
||||
tp := t.In(i)
|
||||
val := reflect.New(tp)
|
||||
pointers[i] = val.Interface()
|
||||
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||
val.Elem().SetString(sender)
|
||||
} else {
|
||||
decode = append(decode, pointers[i])
|
||||
}
|
||||
}
|
||||
if len(decode) != len(vs) {
|
||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||
return
|
||||
}
|
||||
if err := Store(vs, decode...); err != nil {
|
||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
||||
return
|
||||
}
|
||||
params := make([]reflect.Value, len(pointers))
|
||||
for i := 0; i < len(pointers); i++ {
|
||||
params[i] = reflect.ValueOf(pointers[i]).Elem()
|
||||
}
|
||||
ret := m.Call(params)
|
||||
if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
|
||||
conn.sendError(*em, sender, serial)
|
||||
return
|
||||
}
|
||||
if msg.Flags&FlagNoReplyExpected == 0 {
|
||||
reply := new(Message)
|
||||
reply.Type = TypeMethodReply
|
||||
reply.serial = conn.getSerial()
|
||||
reply.Headers = make(map[HeaderField]Variant)
|
||||
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
||||
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
||||
reply.Body = make([]interface{}, len(ret)-1)
|
||||
for i := 0; i < len(ret)-1; i++ {
|
||||
reply.Body[i] = ret[i].Interface()
|
||||
}
|
||||
if len(ret) != 1 {
|
||||
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||
}
|
||||
conn.outLck.RLock()
|
||||
if !conn.closed {
|
||||
conn.out <- reply
|
||||
}
|
||||
conn.outLck.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Emit emits the given signal on the message bus. The name parameter must be
|
||||
// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost".
|
||||
func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error {
|
||||
if !path.IsValid() {
|
||||
return errors.New("dbus: invalid object path")
|
||||
}
|
||||
i := strings.LastIndex(name, ".")
|
||||
if i == -1 {
|
||||
return errors.New("dbus: invalid method name")
|
||||
}
|
||||
iface := name[:i]
|
||||
member := name[i+1:]
|
||||
if !isValidMember(member) {
|
||||
return errors.New("dbus: invalid method name")
|
||||
}
|
||||
if !isValidInterface(iface) {
|
||||
return errors.New("dbus: invalid interface name")
|
||||
}
|
||||
msg := new(Message)
|
||||
msg.Type = TypeSignal
|
||||
msg.serial = conn.getSerial()
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
msg.Headers[FieldInterface] = MakeVariant(iface)
|
||||
msg.Headers[FieldMember] = MakeVariant(member)
|
||||
msg.Headers[FieldPath] = MakeVariant(path)
|
||||
msg.Body = values
|
||||
if len(values) > 0 {
|
||||
msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...))
|
||||
}
|
||||
conn.outLck.RLock()
|
||||
defer conn.outLck.RUnlock()
|
||||
if conn.closed {
|
||||
return ErrClosed
|
||||
}
|
||||
conn.out <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
// Export registers the given value to be exported as an object on the
|
||||
// message bus.
|
||||
//
|
||||
// If a method call on the given path and interface is received, an exported
|
||||
// method with the same name is called with v as the receiver if the
|
||||
// parameters match and the last return value is of type *Error. If this
|
||||
// *Error is not nil, it is sent back to the caller as an error.
|
||||
// Otherwise, a method reply is sent with the other return values as its body.
|
||||
//
|
||||
// Any parameters with the special type Sender are set to the sender of the
|
||||
// dbus message when the method is called. Parameters of this type do not
|
||||
// contribute to the dbus signature of the method (i.e. the method is exposed
|
||||
// as if the parameters of type Sender were not there).
|
||||
//
|
||||
// Every method call is executed in a new goroutine, so the method may be called
|
||||
// in multiple goroutines at once.
|
||||
//
|
||||
// Method calls on the interface org.freedesktop.DBus.Peer will be automatically
|
||||
// handled for every object.
|
||||
//
|
||||
// Passing nil as the first parameter will cause conn to cease handling calls on
|
||||
// the given combination of path and interface.
|
||||
//
|
||||
// Export returns an error if path is not a valid path name.
|
||||
func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error {
|
||||
if !path.IsValid() {
|
||||
return errors.New("dbus: invalid path name")
|
||||
}
|
||||
conn.handlersLck.Lock()
|
||||
if v == nil {
|
||||
if _, ok := conn.handlers[path]; ok {
|
||||
delete(conn.handlers[path], iface)
|
||||
if len(conn.handlers[path]) == 0 {
|
||||
delete(conn.handlers, path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if _, ok := conn.handlers[path]; !ok {
|
||||
conn.handlers[path] = make(map[string]interface{})
|
||||
}
|
||||
conn.handlers[path][iface] = v
|
||||
conn.handlersLck.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseName calls org.freedesktop.DBus.ReleaseName. You should use only this
|
||||
// method to release a name (see below).
|
||||
func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r == uint32(ReleaseNameReplyReleased) {
|
||||
conn.namesLck.Lock()
|
||||
for i, v := range conn.names {
|
||||
if v == name {
|
||||
copy(conn.names[i:], conn.names[i+1:])
|
||||
conn.names = conn.names[:len(conn.names)-1]
|
||||
}
|
||||
}
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
return ReleaseNameReply(r), nil
|
||||
}
|
||||
|
||||
// RequestName calls org.freedesktop.DBus.RequestName. You should use only this
|
||||
// method to request a name because package dbus needs to keep track of all
|
||||
// names that the connection has.
|
||||
func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) {
|
||||
var r uint32
|
||||
err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r == uint32(RequestNameReplyPrimaryOwner) {
|
||||
conn.namesLck.Lock()
|
||||
conn.names = append(conn.names, name)
|
||||
conn.namesLck.Unlock()
|
||||
}
|
||||
return RequestNameReply(r), nil
|
||||
}
|
||||
|
||||
// ReleaseNameReply is the reply to a ReleaseName call.
|
||||
type ReleaseNameReply uint32
|
||||
|
||||
const (
|
||||
ReleaseNameReplyReleased ReleaseNameReply = 1 + iota
|
||||
ReleaseNameReplyNonExistent
|
||||
ReleaseNameReplyNotOwner
|
||||
)
|
||||
|
||||
// RequestNameFlags represents the possible flags for a RequestName call.
|
||||
type RequestNameFlags uint32
|
||||
|
||||
const (
|
||||
NameFlagAllowReplacement RequestNameFlags = 1 << iota
|
||||
NameFlagReplaceExisting
|
||||
NameFlagDoNotQueue
|
||||
)
|
||||
|
||||
// RequestNameReply is the reply to a RequestName call.
|
||||
type RequestNameReply uint32
|
||||
|
||||
const (
|
||||
RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota
|
||||
RequestNameReplyInQueue
|
||||
RequestNameReplyExists
|
||||
RequestNameReplyAlreadyOwner
|
||||
)
|
||||
27
third_party/github.com/guelfey/go.dbus/introspect/call.go
vendored
Normal file
27
third_party/github.com/guelfey/go.dbus/introspect/call.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package introspect
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Call calls org.freedesktop.Introspectable.Introspect on a remote object
|
||||
// and returns the introspection data.
|
||||
func Call(o *dbus.Object) (*Node, error) {
|
||||
var xmldata string
|
||||
var node Node
|
||||
|
||||
err := o.Call("org.freedesktop.DBus.Introspectable.Introspect", 0).Store(&xmldata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = xml.NewDecoder(strings.NewReader(xmldata)).Decode(&node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node.Name == "" {
|
||||
node.Name = string(o.Path())
|
||||
}
|
||||
return &node, nil
|
||||
}
|
||||
80
third_party/github.com/guelfey/go.dbus/introspect/introspect.go
vendored
Normal file
80
third_party/github.com/guelfey/go.dbus/introspect/introspect.go
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Package introspect provides some utilities for dealing with the DBus
|
||||
// introspection format.
|
||||
package introspect
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Introspectable interface.
|
||||
var IntrospectData = Interface{
|
||||
Name: "org.freedesktop.DBus.Introspectable",
|
||||
Methods: []Method{
|
||||
{
|
||||
Name: "Introspect",
|
||||
Args: []Arg{
|
||||
{"out", "s", "out"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Introspectable interface,
|
||||
// as a string.
|
||||
const IntrospectDataString = `
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Introspect">
|
||||
<arg name="out" direction="out" type="s"/>
|
||||
</method>
|
||||
</interface>
|
||||
`
|
||||
|
||||
// Node is the root element of an introspection.
|
||||
type Node struct {
|
||||
XMLName xml.Name `xml:"node"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Interfaces []Interface `xml:"interface"`
|
||||
Children []Node `xml:"node,omitempty"`
|
||||
}
|
||||
|
||||
// Interface describes a DBus interface that is available on the message bus.
|
||||
type Interface struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Methods []Method `xml:"method"`
|
||||
Signals []Signal `xml:"signal"`
|
||||
Properties []Property `xml:"property"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Method describes a Method on an Interface as retured by an introspection.
|
||||
type Method struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Args []Arg `xml:"arg"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Signal describes a Signal emitted on an Interface.
|
||||
type Signal struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Args []Arg `xml:"arg"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Property describes a property of an Interface.
|
||||
type Property struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Access string `xml:"access,attr"`
|
||||
Annotations []Annotation `xml:"annotation"`
|
||||
}
|
||||
|
||||
// Arg represents an argument of a method or a signal.
|
||||
type Arg struct {
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Type string `xml:"type,attr"`
|
||||
Direction string `xml:"direction,attr,omitempty"`
|
||||
}
|
||||
|
||||
// Annotation is an annotation in the introspection format.
|
||||
type Annotation struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
74
third_party/github.com/guelfey/go.dbus/introspect/introspectable.go
vendored
Normal file
74
third_party/github.com/guelfey/go.dbus/introspect/introspectable.go
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package introspect
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Introspectable implements org.freedesktop.Introspectable.
|
||||
//
|
||||
// You can create it by converting the XML-formatted introspection data from a
|
||||
// string to an Introspectable or call NewIntrospectable with a Node. Then,
|
||||
// export it as org.freedesktop.Introspectable on you object.
|
||||
type Introspectable string
|
||||
|
||||
// NewIntrospectable returns an Introspectable that returns the introspection
|
||||
// data that corresponds to the given Node. If n.Interfaces doesn't contain the
|
||||
// data for org.freedesktop.DBus.Introspectable, it is added automatically.
|
||||
func NewIntrospectable(n *Node) Introspectable {
|
||||
found := false
|
||||
for _, v := range n.Interfaces {
|
||||
if v.Name == "org.freedesktop.DBus.Introspectable" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
n.Interfaces = append(n.Interfaces, IntrospectData)
|
||||
}
|
||||
b, err := xml.Marshal(n)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Introspectable(b)
|
||||
}
|
||||
|
||||
// Introspect implements org.freedesktop.Introspectable.Introspect.
|
||||
func (i Introspectable) Introspect() (string, *dbus.Error) {
|
||||
return string(i), nil
|
||||
}
|
||||
|
||||
// Methods returns the description of the methods of v. This can be used to
|
||||
// create a Node which can be passed to NewIntrospectable.
|
||||
func Methods(v interface{}) []Method {
|
||||
t := reflect.TypeOf(v)
|
||||
ms := make([]Method, 0, t.NumMethod())
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
if t.Method(i).PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
mt := t.Method(i).Type
|
||||
if mt.NumOut() == 0 ||
|
||||
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{"", nil}) {
|
||||
|
||||
continue
|
||||
}
|
||||
var m Method
|
||||
m.Name = t.Method(i).Name
|
||||
m.Args = make([]Arg, 0, mt.NumIn()+mt.NumOut()-2)
|
||||
for j := 1; j < mt.NumIn(); j++ {
|
||||
if mt.In(j) != reflect.TypeOf((*dbus.Sender)(nil)).Elem() {
|
||||
arg := Arg{"", dbus.SignatureOfType(mt.In(j)).String(), "in"}
|
||||
m.Args = append(m.Args, arg)
|
||||
}
|
||||
}
|
||||
for j := 0; j < mt.NumOut()-1; j++ {
|
||||
arg := Arg{"", dbus.SignatureOfType(mt.Out(j)).String(), "out"}
|
||||
m.Args = append(m.Args, arg)
|
||||
}
|
||||
m.Annotations = make([]Annotation, 0)
|
||||
ms = append(ms, m)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
346
third_party/github.com/guelfey/go.dbus/message.go
vendored
Normal file
346
third_party/github.com/guelfey/go.dbus/message.go
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const protoVersion byte = 1
|
||||
|
||||
// Flags represents the possible flags of a D-Bus message.
|
||||
type Flags byte
|
||||
|
||||
const (
|
||||
// FlagNoReplyExpected signals that the message is not expected to generate
|
||||
// a reply. If this flag is set on outgoing messages, any possible reply
|
||||
// will be discarded.
|
||||
FlagNoReplyExpected Flags = 1 << iota
|
||||
// FlagNoAutoStart signals that the message bus should not automatically
|
||||
// start an application when handling this message.
|
||||
FlagNoAutoStart
|
||||
)
|
||||
|
||||
// Type represents the possible types of a D-Bus message.
|
||||
type Type byte
|
||||
|
||||
const (
|
||||
TypeMethodCall Type = 1 + iota
|
||||
TypeMethodReply
|
||||
TypeError
|
||||
TypeSignal
|
||||
typeMax
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeMethodCall:
|
||||
return "method call"
|
||||
case TypeMethodReply:
|
||||
return "reply"
|
||||
case TypeError:
|
||||
return "error"
|
||||
case TypeSignal:
|
||||
return "signal"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
|
||||
// HeaderField represents the possible byte codes for the headers
|
||||
// of a D-Bus message.
|
||||
type HeaderField byte
|
||||
|
||||
const (
|
||||
FieldPath HeaderField = 1 + iota
|
||||
FieldInterface
|
||||
FieldMember
|
||||
FieldErrorName
|
||||
FieldReplySerial
|
||||
FieldDestination
|
||||
FieldSender
|
||||
FieldSignature
|
||||
FieldUnixFDs
|
||||
fieldMax
|
||||
)
|
||||
|
||||
// An InvalidMessageError describes the reason why a D-Bus message is regarded as
|
||||
// invalid.
|
||||
type InvalidMessageError string
|
||||
|
||||
func (e InvalidMessageError) Error() string {
|
||||
return "dbus: invalid message: " + string(e)
|
||||
}
|
||||
|
||||
// fieldType are the types of the various header fields.
|
||||
var fieldTypes = [fieldMax]reflect.Type{
|
||||
FieldPath: objectPathType,
|
||||
FieldInterface: stringType,
|
||||
FieldMember: stringType,
|
||||
FieldErrorName: stringType,
|
||||
FieldReplySerial: uint32Type,
|
||||
FieldDestination: stringType,
|
||||
FieldSender: stringType,
|
||||
FieldSignature: signatureType,
|
||||
FieldUnixFDs: uint32Type,
|
||||
}
|
||||
|
||||
// requiredFields lists the header fields that are required by the different
|
||||
// message types.
|
||||
var requiredFields = [typeMax][]HeaderField{
|
||||
TypeMethodCall: {FieldPath, FieldMember},
|
||||
TypeMethodReply: {FieldReplySerial},
|
||||
TypeError: {FieldErrorName, FieldReplySerial},
|
||||
TypeSignal: {FieldPath, FieldInterface, FieldMember},
|
||||
}
|
||||
|
||||
// Message represents a single D-Bus message.
|
||||
type Message struct {
|
||||
Type
|
||||
Flags
|
||||
Headers map[HeaderField]Variant
|
||||
Body []interface{}
|
||||
|
||||
serial uint32
|
||||
}
|
||||
|
||||
type header struct {
|
||||
Field byte
|
||||
Variant
|
||||
}
|
||||
|
||||
// DecodeMessage tries to decode a single message in the D-Bus wire format
|
||||
// from the given reader. The byte order is figured out from the first byte.
|
||||
// The possibly returned error can be an error of the underlying reader, an
|
||||
// InvalidMessageError or a FormatError.
|
||||
func DecodeMessage(rd io.Reader) (msg *Message, err error) {
|
||||
var order binary.ByteOrder
|
||||
var hlength, length uint32
|
||||
var typ, flags, proto byte
|
||||
var headers []header
|
||||
|
||||
b := make([]byte, 1)
|
||||
_, err = rd.Read(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch b[0] {
|
||||
case 'l':
|
||||
order = binary.LittleEndian
|
||||
case 'B':
|
||||
order = binary.BigEndian
|
||||
default:
|
||||
return nil, InvalidMessageError("invalid byte order")
|
||||
}
|
||||
|
||||
dec := newDecoder(rd, order)
|
||||
dec.pos = 1
|
||||
|
||||
msg = new(Message)
|
||||
vs, err := dec.Decode(Signature{"yyyuu"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = Store(vs, &typ, &flags, &proto, &length, &msg.serial); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg.Type = Type(typ)
|
||||
msg.Flags = Flags(flags)
|
||||
|
||||
// get the header length separately because we need it later
|
||||
b = make([]byte, 4)
|
||||
_, err = io.ReadFull(rd, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.Read(bytes.NewBuffer(b), order, &hlength)
|
||||
if hlength+length+16 > 1<<27 {
|
||||
return nil, InvalidMessageError("message is too long")
|
||||
}
|
||||
dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order)
|
||||
dec.pos = 12
|
||||
vs, err = dec.Decode(Signature{"a(yv)"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = Store(vs, &headers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg.Headers = make(map[HeaderField]Variant)
|
||||
for _, v := range headers {
|
||||
msg.Headers[HeaderField(v.Field)] = v.Variant
|
||||
}
|
||||
|
||||
dec.align(8)
|
||||
body := make([]byte, int(length))
|
||||
if length != 0 {
|
||||
_, err := io.ReadFull(rd, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = msg.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig, _ := msg.Headers[FieldSignature].value.(Signature)
|
||||
if sig.str != "" {
|
||||
buf := bytes.NewBuffer(body)
|
||||
dec = newDecoder(buf, order)
|
||||
vs, err := dec.Decode(sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg.Body = vs
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeTo encodes and sends a message to the given writer. The byte order must
|
||||
// be either binary.LittleEndian or binary.BigEndian. If the message is not
|
||||
// valid or an error occurs when writing, an error is returned.
|
||||
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error {
|
||||
if err := msg.IsValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
var vs [7]interface{}
|
||||
switch order {
|
||||
case binary.LittleEndian:
|
||||
vs[0] = byte('l')
|
||||
case binary.BigEndian:
|
||||
vs[0] = byte('B')
|
||||
default:
|
||||
return errors.New("dbus: invalid byte order")
|
||||
}
|
||||
body := new(bytes.Buffer)
|
||||
enc := newEncoder(body, order)
|
||||
if len(msg.Body) != 0 {
|
||||
enc.Encode(msg.Body...)
|
||||
}
|
||||
vs[1] = msg.Type
|
||||
vs[2] = msg.Flags
|
||||
vs[3] = protoVersion
|
||||
vs[4] = uint32(len(body.Bytes()))
|
||||
vs[5] = msg.serial
|
||||
headers := make([]header, 0, len(msg.Headers))
|
||||
for k, v := range msg.Headers {
|
||||
headers = append(headers, header{byte(k), v})
|
||||
}
|
||||
vs[6] = headers
|
||||
var buf bytes.Buffer
|
||||
enc = newEncoder(&buf, order)
|
||||
enc.Encode(vs[:]...)
|
||||
enc.align(8)
|
||||
body.WriteTo(&buf)
|
||||
if buf.Len() > 1<<27 {
|
||||
return InvalidMessageError("message is too long")
|
||||
}
|
||||
if _, err := buf.WriteTo(out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValid checks whether msg is a valid message and returns an
|
||||
// InvalidMessageError if it is not.
|
||||
func (msg *Message) IsValid() error {
|
||||
if msg.Flags & ^(FlagNoAutoStart|FlagNoReplyExpected) != 0 {
|
||||
return InvalidMessageError("invalid flags")
|
||||
}
|
||||
if msg.Type == 0 || msg.Type >= typeMax {
|
||||
return InvalidMessageError("invalid message type")
|
||||
}
|
||||
for k, v := range msg.Headers {
|
||||
if k == 0 || k >= fieldMax {
|
||||
return InvalidMessageError("invalid header")
|
||||
}
|
||||
if reflect.TypeOf(v.value) != fieldTypes[k] {
|
||||
return InvalidMessageError("invalid type of header field")
|
||||
}
|
||||
}
|
||||
for _, v := range requiredFields[msg.Type] {
|
||||
if _, ok := msg.Headers[v]; !ok {
|
||||
return InvalidMessageError("missing required header")
|
||||
}
|
||||
}
|
||||
if path, ok := msg.Headers[FieldPath]; ok {
|
||||
if !path.value.(ObjectPath).IsValid() {
|
||||
return InvalidMessageError("invalid path name")
|
||||
}
|
||||
}
|
||||
if iface, ok := msg.Headers[FieldInterface]; ok {
|
||||
if !isValidInterface(iface.value.(string)) {
|
||||
return InvalidMessageError("invalid interface name")
|
||||
}
|
||||
}
|
||||
if member, ok := msg.Headers[FieldMember]; ok {
|
||||
if !isValidMember(member.value.(string)) {
|
||||
return InvalidMessageError("invalid member name")
|
||||
}
|
||||
}
|
||||
if errname, ok := msg.Headers[FieldErrorName]; ok {
|
||||
if !isValidInterface(errname.value.(string)) {
|
||||
return InvalidMessageError("invalid error name")
|
||||
}
|
||||
}
|
||||
if len(msg.Body) != 0 {
|
||||
if _, ok := msg.Headers[FieldSignature]; !ok {
|
||||
return InvalidMessageError("missing signature")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serial returns the message's serial number. The returned value is only valid
|
||||
// for messages received by eavesdropping.
|
||||
func (msg *Message) Serial() uint32 {
|
||||
return msg.serial
|
||||
}
|
||||
|
||||
// String returns a string representation of a message similar to the format of
|
||||
// dbus-monitor.
|
||||
func (msg *Message) String() string {
|
||||
if err := msg.IsValid(); err != nil {
|
||||
return "<invalid>"
|
||||
}
|
||||
s := msg.Type.String()
|
||||
if v, ok := msg.Headers[FieldSender]; ok {
|
||||
s += " from " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldDestination]; ok {
|
||||
s += " to " + v.value.(string)
|
||||
}
|
||||
s += " serial " + strconv.FormatUint(uint64(msg.serial), 10)
|
||||
if v, ok := msg.Headers[FieldReplySerial]; ok {
|
||||
s += " reply_serial " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldUnixFDs]; ok {
|
||||
s += " unixfds " + strconv.FormatUint(uint64(v.value.(uint32)), 10)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldPath]; ok {
|
||||
s += " path " + string(v.value.(ObjectPath))
|
||||
}
|
||||
if v, ok := msg.Headers[FieldInterface]; ok {
|
||||
s += " interface " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldErrorName]; ok {
|
||||
s += " error " + v.value.(string)
|
||||
}
|
||||
if v, ok := msg.Headers[FieldMember]; ok {
|
||||
s += " member " + v.value.(string)
|
||||
}
|
||||
if len(msg.Body) != 0 {
|
||||
s += "\n"
|
||||
}
|
||||
for i, v := range msg.Body {
|
||||
s += " " + MakeVariant(v).String()
|
||||
if i != len(msg.Body)-1 {
|
||||
s += "\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
264
third_party/github.com/guelfey/go.dbus/prop/prop.go
vendored
Normal file
264
third_party/github.com/guelfey/go.dbus/prop/prop.go
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
// Package prop provides the Properties struct which can be used to implement
|
||||
// org.freedesktop.DBus.Properties.
|
||||
package prop
|
||||
|
||||
import (
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus/introspect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// EmitType controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||
// emitted for a property. If it is EmitTrue, the signal is emitted. If it is
|
||||
// EmitInvalidates, the signal is also emitted, but the new value of the property
|
||||
// is not disclosed.
|
||||
type EmitType byte
|
||||
|
||||
const (
|
||||
EmitFalse EmitType = iota
|
||||
EmitTrue
|
||||
EmitInvalidates
|
||||
)
|
||||
|
||||
// ErrIfaceNotFound is the error returned to peers who try to access properties
|
||||
// on interfaces that aren't found.
|
||||
var ErrIfaceNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil}
|
||||
|
||||
// ErrPropNotFound is the error returned to peers trying to access properties
|
||||
// that aren't found.
|
||||
var ErrPropNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil}
|
||||
|
||||
// ErrReadOnly is the error returned to peers trying to set a read-only
|
||||
// property.
|
||||
var ErrReadOnly = &dbus.Error{"org.freedesktop.DBus.Properties.Error.ReadOnly", nil}
|
||||
|
||||
// ErrInvalidArg is returned to peers if the type of the property that is being
|
||||
// changed and the argument don't match.
|
||||
var ErrInvalidArg = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InvalidArg", nil}
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Properties interface.
|
||||
var IntrospectData = introspect.Interface{
|
||||
Name: "org.freedesktop.DBus.Properties",
|
||||
Methods: []introspect.Method{
|
||||
{
|
||||
Name: "Get",
|
||||
Args: []introspect.Arg{
|
||||
{"interface", "s", "in"},
|
||||
{"property", "s", "in"},
|
||||
{"value", "v", "out"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "GetAll",
|
||||
Args: []introspect.Arg{
|
||||
{"interface", "s", "in"},
|
||||
{"props", "a{sv}", "out"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Set",
|
||||
Args: []introspect.Arg{
|
||||
{"interface", "s", "in"},
|
||||
{"property", "s", "in"},
|
||||
{"value", "v", "in"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Signals: []introspect.Signal{
|
||||
{
|
||||
Name: "PropertiesChanged",
|
||||
Args: []introspect.Arg{
|
||||
{"interface", "s", "out"},
|
||||
{"changed_properties", "a{sv}", "out"},
|
||||
{"invalidates_properties", "as", "out"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Properties interface, as
|
||||
// a string.
|
||||
const IntrospectDataString = `
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Get">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="out" type="v"/>
|
||||
</method>
|
||||
<method name="GetAll">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="props" direction="out" type="a{sv}"/>
|
||||
</method>
|
||||
<method name="Set">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="in" type="v"/>
|
||||
</method>
|
||||
<signal name="PropertiesChanged">
|
||||
<arg name="interface" type="s"/>
|
||||
<arg name="changed_properties" type="a{sv}"/>
|
||||
<arg name="invalidates_properties" type="as"/>
|
||||
</signal>
|
||||
</interface>
|
||||
`
|
||||
|
||||
// Prop represents a single property. It is used for creating a Properties
|
||||
// value.
|
||||
type Prop struct {
|
||||
// Initial value. Must be a DBus-representable type.
|
||||
Value interface{}
|
||||
|
||||
// If true, the value can be modified by calls to Set.
|
||||
Writable bool
|
||||
|
||||
// Controls how org.freedesktop.DBus.Properties.PropertiesChanged is
|
||||
// emitted if this property changes.
|
||||
Emit EmitType
|
||||
|
||||
// If not nil, anytime this property is changed by Set, this function is
|
||||
// called with an appropiate Change as its argument. If the returned error
|
||||
// is not nil, it is sent back to the caller of Set and the property is not
|
||||
// changed.
|
||||
Callback func(*Change) *dbus.Error
|
||||
}
|
||||
|
||||
// Change represents a change of a property by a call to Set.
|
||||
type Change struct {
|
||||
Props *Properties
|
||||
Iface string
|
||||
Name string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Properties is a set of values that can be made available to the message bus
|
||||
// using the org.freedesktop.DBus.Properties interface. It is safe for
|
||||
// concurrent use by multiple goroutines.
|
||||
type Properties struct {
|
||||
m map[string]map[string]*Prop
|
||||
mut sync.RWMutex
|
||||
conn *dbus.Conn
|
||||
path dbus.ObjectPath
|
||||
}
|
||||
|
||||
// New returns a new Properties structure that manages the given properties.
|
||||
// The key for the first-level map of props is the name of the interface; the
|
||||
// second-level key is the name of the property. The returned structure will be
|
||||
// exported as org.freedesktop.DBus.Properties on path.
|
||||
func New(conn *dbus.Conn, path dbus.ObjectPath, props map[string]map[string]*Prop) *Properties {
|
||||
p := &Properties{m: props, conn: conn, path: path}
|
||||
conn.Export(p, path, "org.freedesktop.DBus.Properties")
|
||||
return p
|
||||
}
|
||||
|
||||
// Get implements org.freedesktop.DBus.Properties.Get.
|
||||
func (p *Properties) Get(iface, property string) (dbus.Variant, *dbus.Error) {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return dbus.Variant{}, ErrIfaceNotFound
|
||||
}
|
||||
prop, ok := m[property]
|
||||
if !ok {
|
||||
return dbus.Variant{}, ErrPropNotFound
|
||||
}
|
||||
return dbus.MakeVariant(prop.Value), nil
|
||||
}
|
||||
|
||||
// GetAll implements org.freedesktop.DBus.Properties.GetAll.
|
||||
func (p *Properties) GetAll(iface string) (map[string]dbus.Variant, *dbus.Error) {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return nil, ErrIfaceNotFound
|
||||
}
|
||||
rm := make(map[string]dbus.Variant, len(m))
|
||||
for k, v := range m {
|
||||
rm[k] = dbus.MakeVariant(v.Value)
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
// GetMust returns the value of the given property and panics if either the
|
||||
// interface or the property name are invalid.
|
||||
func (p *Properties) GetMust(iface, property string) interface{} {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
return p.m[iface][property].Value
|
||||
}
|
||||
|
||||
// Introspection returns the introspection data that represents the properties
|
||||
// of iface.
|
||||
func (p *Properties) Introspection(iface string) []introspect.Property {
|
||||
p.mut.RLock()
|
||||
defer p.mut.RUnlock()
|
||||
m := p.m[iface]
|
||||
s := make([]introspect.Property, 0, len(m))
|
||||
for k, v := range m {
|
||||
p := introspect.Property{Name: k, Type: dbus.SignatureOf(v.Value).String()}
|
||||
if v.Writable {
|
||||
p.Access = "readwrite"
|
||||
} else {
|
||||
p.Access = "read"
|
||||
}
|
||||
s = append(s, p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// set sets the given property and emits PropertyChanged if appropiate. p.mut
|
||||
// must already be locked.
|
||||
func (p *Properties) set(iface, property string, v interface{}) {
|
||||
prop := p.m[iface][property]
|
||||
prop.Value = v
|
||||
switch prop.Emit {
|
||||
case EmitFalse:
|
||||
// do nothing
|
||||
case EmitInvalidates:
|
||||
p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||
iface, map[string]dbus.Variant{}, []string{property})
|
||||
case EmitTrue:
|
||||
p.conn.Emit(p.path, "org.freedesktop.DBus.Properties.PropertiesChanged",
|
||||
iface, map[string]dbus.Variant{property: dbus.MakeVariant(v)},
|
||||
[]string{})
|
||||
default:
|
||||
panic("invalid value for EmitType")
|
||||
}
|
||||
}
|
||||
|
||||
// Set implements org.freedesktop.Properties.Set.
|
||||
func (p *Properties) Set(iface, property string, newv dbus.Variant) *dbus.Error {
|
||||
p.mut.Lock()
|
||||
defer p.mut.Unlock()
|
||||
m, ok := p.m[iface]
|
||||
if !ok {
|
||||
return ErrIfaceNotFound
|
||||
}
|
||||
prop, ok := m[property]
|
||||
if !ok {
|
||||
return ErrPropNotFound
|
||||
}
|
||||
if !prop.Writable {
|
||||
return ErrReadOnly
|
||||
}
|
||||
if newv.Signature() != dbus.SignatureOf(prop.Value) {
|
||||
return ErrInvalidArg
|
||||
}
|
||||
if prop.Callback != nil {
|
||||
err := prop.Callback(&Change{p, iface, property, newv.Value()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.set(iface, property, newv.Value())
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMust sets the value of the given property and panics if the interface or
|
||||
// the property name are invalid.
|
||||
func (p *Properties) SetMust(iface, property string, v interface{}) {
|
||||
p.mut.Lock()
|
||||
p.set(iface, property, v)
|
||||
p.mut.Unlock()
|
||||
}
|
||||
369
third_party/github.com/guelfey/go.dbus/proto_test.go
vendored
Normal file
369
third_party/github.com/guelfey/go.dbus/proto_test.go
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var protoTests = []struct {
|
||||
vs []interface{}
|
||||
bigEndian []byte
|
||||
littleEndian []byte
|
||||
}{
|
||||
{
|
||||
[]interface{}{int32(0)},
|
||||
[]byte{0, 0, 0, 0},
|
||||
[]byte{0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{true, false},
|
||||
[]byte{0, 0, 0, 1, 0, 0, 0, 0},
|
||||
[]byte{1, 0, 0, 0, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{byte(0), uint16(12), int16(32), uint32(43)},
|
||||
[]byte{0, 0, 0, 12, 0, 32, 0, 0, 0, 0, 0, 43},
|
||||
[]byte{0, 0, 12, 0, 32, 0, 0, 0, 43, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{int64(-1), uint64(1<<64 - 1)},
|
||||
bytes.Repeat([]byte{255}, 16),
|
||||
bytes.Repeat([]byte{255}, 16),
|
||||
},
|
||||
{
|
||||
[]interface{}{math.Inf(+1)},
|
||||
[]byte{0x7f, 0xf0, 0, 0, 0, 0, 0, 0},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0xf0, 0x7f},
|
||||
},
|
||||
{
|
||||
[]interface{}{"foo"},
|
||||
[]byte{0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||
[]byte{3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{Signature{"ai"}},
|
||||
[]byte{2, 'a', 'i', 0},
|
||||
[]byte{2, 'a', 'i', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]int16{42, 256}},
|
||||
[]byte{0, 0, 0, 4, 0, 42, 1, 0},
|
||||
[]byte{4, 0, 0, 0, 42, 0, 0, 1},
|
||||
},
|
||||
{
|
||||
[]interface{}{MakeVariant("foo")},
|
||||
[]byte{1, 's', 0, 0, 0, 0, 0, 3, 'f', 'o', 'o', 0},
|
||||
[]byte{1, 's', 0, 0, 3, 0, 0, 0, 'f', 'o', 'o', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{MakeVariant(MakeVariant(Signature{"v"}))},
|
||||
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{map[int32]bool{42: true}},
|
||||
[]byte{0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1},
|
||||
[]byte{8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
[]interface{}{map[string]Variant{}, byte(42)},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]uint64{}, byte(42)},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
|
||||
},
|
||||
}
|
||||
|
||||
func TestProto(t *testing.T) {
|
||||
for i, v := range protoTests {
|
||||
buf := new(bytes.Buffer)
|
||||
bigEnc := newEncoder(buf, binary.BigEndian)
|
||||
bigEnc.Encode(v.vs...)
|
||||
marshalled := buf.Bytes()
|
||||
if bytes.Compare(marshalled, v.bigEndian) != 0 {
|
||||
t.Errorf("test %d (marshal be): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||
v.bigEndian)
|
||||
}
|
||||
buf.Reset()
|
||||
litEnc := newEncoder(buf, binary.LittleEndian)
|
||||
litEnc.Encode(v.vs...)
|
||||
marshalled = buf.Bytes()
|
||||
if bytes.Compare(marshalled, v.littleEndian) != 0 {
|
||||
t.Errorf("test %d (marshal le): got '%v', but expected '%v'\n", i+1, marshalled,
|
||||
v.littleEndian)
|
||||
}
|
||||
unmarshalled := reflect.MakeSlice(reflect.TypeOf(v.vs),
|
||||
0, 0)
|
||||
for i := range v.vs {
|
||||
unmarshalled = reflect.Append(unmarshalled,
|
||||
reflect.New(reflect.TypeOf(v.vs[i])))
|
||||
}
|
||||
bigDec := newDecoder(bytes.NewReader(v.bigEndian), binary.BigEndian)
|
||||
vs, err := bigDec.Decode(SignatureOf(v.vs...))
|
||||
if err != nil {
|
||||
t.Errorf("test %d (unmarshal be): %s\n", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(vs, v.vs) {
|
||||
t.Errorf("test %d (unmarshal be): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||
}
|
||||
litDec := newDecoder(bytes.NewReader(v.littleEndian), binary.LittleEndian)
|
||||
vs, err = litDec.Decode(SignatureOf(v.vs...))
|
||||
if err != nil {
|
||||
t.Errorf("test %d (unmarshal le): %s\n", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(vs, v.vs) {
|
||||
t.Errorf("test %d (unmarshal le): got %#v, but expected %#v\n", i+1, vs, v.vs)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoMap(t *testing.T) {
|
||||
m := map[string]uint8{
|
||||
"foo": 23,
|
||||
"bar": 2,
|
||||
}
|
||||
var n map[string]uint8
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(m)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"a{sy}"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(n) != 2 || n["foo"] != 23 || n["bar"] != 2 {
|
||||
t.Error("got", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoVariantStruct(t *testing.T) {
|
||||
var variant Variant
|
||||
v := MakeVariant(struct {
|
||||
A int32
|
||||
B int16
|
||||
}{1, 2})
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(v)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"v"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &variant); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sl := variant.Value().([]interface{})
|
||||
v1, v2 := sl[0].(int32), sl[1].(int16)
|
||||
if v1 != int32(1) {
|
||||
t.Error("got", v1, "as first int")
|
||||
}
|
||||
if v2 != int16(2) {
|
||||
t.Error("got", v2, "as second int")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStructTag(t *testing.T) {
|
||||
type Bar struct {
|
||||
A int32
|
||||
B chan interface{} `dbus:"-"`
|
||||
C int32
|
||||
}
|
||||
var bar1, bar2 Bar
|
||||
bar1.A = 234
|
||||
bar2.C = 345
|
||||
buf := new(bytes.Buffer)
|
||||
enc := newEncoder(buf, binary.LittleEndian)
|
||||
enc.Encode(bar1)
|
||||
dec := newDecoder(buf, binary.LittleEndian)
|
||||
vs, err := dec.Decode(Signature{"(ii)"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = Store(vs, &bar2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bar1 != bar2 {
|
||||
t.Error("struct tag test: got", bar2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStoreStruct(t *testing.T) {
|
||||
var foo struct {
|
||||
A int32
|
||||
B string
|
||||
c chan interface{}
|
||||
D interface{} `dbus:"-"`
|
||||
}
|
||||
src := []interface{}{[]interface{}{int32(42), "foo"}}
|
||||
err := Store(src, &foo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStoreNestedStruct(t *testing.T) {
|
||||
var foo struct {
|
||||
A int32
|
||||
B struct {
|
||||
C string
|
||||
D float64
|
||||
}
|
||||
}
|
||||
src := []interface{}{
|
||||
[]interface{}{
|
||||
int32(42),
|
||||
[]interface{}{
|
||||
"foo",
|
||||
3.14,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := Store(src, &foo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
message := new(Message)
|
||||
message.Type = TypeMethodCall
|
||||
message.serial = 32
|
||||
message.Headers = map[HeaderField]Variant{
|
||||
FieldPath: MakeVariant(ObjectPath("/org/foo/bar")),
|
||||
FieldMember: MakeVariant("baz"),
|
||||
}
|
||||
message.Body = make([]interface{}, 0)
|
||||
err := message.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = DecodeMessage(buf)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtoStructInterfaces(t *testing.T) {
|
||||
b := []byte{42}
|
||||
vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).Decode(Signature{"(y)"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if vs[0].([]interface{})[0].(byte) != 42 {
|
||||
t.Errorf("wrongs results (got %v)", vs)
|
||||
}
|
||||
}
|
||||
|
||||
// ordinary org.freedesktop.DBus.Hello call
|
||||
var smallMessage = &Message{
|
||||
Type: TypeMethodCall,
|
||||
serial: 1,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant("org.freedesktop.DBus"),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.DBus"),
|
||||
FieldMember: MakeVariant("Hello"),
|
||||
},
|
||||
}
|
||||
|
||||
// org.freedesktop.Notifications.Notify
|
||||
var bigMessage = &Message{
|
||||
Type: TypeMethodCall,
|
||||
serial: 2,
|
||||
Headers: map[HeaderField]Variant{
|
||||
FieldDestination: MakeVariant("org.freedesktop.Notifications"),
|
||||
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/Notifications")),
|
||||
FieldInterface: MakeVariant("org.freedesktop.Notifications"),
|
||||
FieldMember: MakeVariant("Notify"),
|
||||
FieldSignature: MakeVariant(Signature{"susssasa{sv}i"}),
|
||||
},
|
||||
Body: []interface{}{
|
||||
"app_name",
|
||||
uint32(0),
|
||||
"dialog-information",
|
||||
"Notification",
|
||||
"This is the body of a notification",
|
||||
[]string{"ok", "Ok"},
|
||||
map[string]Variant{
|
||||
"sound-name": MakeVariant("dialog-information"),
|
||||
},
|
||||
int32(-1),
|
||||
},
|
||||
}
|
||||
|
||||
func BenchmarkDecodeMessageSmall(b *testing.B) {
|
||||
var err error
|
||||
var rd *bytes.Reader
|
||||
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
err = smallMessage.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
decoded := buf.Bytes()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rd = bytes.NewReader(decoded)
|
||||
_, err = DecodeMessage(rd)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeMessageBig(b *testing.B) {
|
||||
var err error
|
||||
var rd *bytes.Reader
|
||||
|
||||
b.StopTimer()
|
||||
buf := new(bytes.Buffer)
|
||||
err = bigMessage.EncodeTo(buf, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
decoded := buf.Bytes()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rd = bytes.NewReader(decoded)
|
||||
_, err = DecodeMessage(rd)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeMessageSmall(b *testing.B) {
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = smallMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeMessageBig(b *testing.B) {
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
err = bigMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
257
third_party/github.com/guelfey/go.dbus/sig.go
vendored
Normal file
257
third_party/github.com/guelfey/go.dbus/sig.go
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var sigToType = map[byte]reflect.Type{
|
||||
'y': byteType,
|
||||
'b': boolType,
|
||||
'n': int16Type,
|
||||
'q': uint16Type,
|
||||
'i': int32Type,
|
||||
'u': uint32Type,
|
||||
'x': int64Type,
|
||||
't': uint64Type,
|
||||
'd': float64Type,
|
||||
's': stringType,
|
||||
'g': signatureType,
|
||||
'o': objectPathType,
|
||||
'v': variantType,
|
||||
'h': unixFDIndexType,
|
||||
}
|
||||
|
||||
// Signature represents a correct type signature as specified by the D-Bus
|
||||
// specification. The zero value represents the empty signature, "".
|
||||
type Signature struct {
|
||||
str string
|
||||
}
|
||||
|
||||
// SignatureOf returns the concatenation of all the signatures of the given
|
||||
// values. It panics if one of them is not representable in D-Bus.
|
||||
func SignatureOf(vs ...interface{}) Signature {
|
||||
var s string
|
||||
for _, v := range vs {
|
||||
s += getSignature(reflect.TypeOf(v))
|
||||
}
|
||||
return Signature{s}
|
||||
}
|
||||
|
||||
// SignatureOfType returns the signature of the given type. It panics if the
|
||||
// type is not representable in D-Bus.
|
||||
func SignatureOfType(t reflect.Type) Signature {
|
||||
return Signature{getSignature(t)}
|
||||
}
|
||||
|
||||
// getSignature returns the signature of the given type and panics on unknown types.
|
||||
func getSignature(t reflect.Type) string {
|
||||
// handle simple types first
|
||||
switch t.Kind() {
|
||||
case reflect.Uint8:
|
||||
return "y"
|
||||
case reflect.Bool:
|
||||
return "b"
|
||||
case reflect.Int16:
|
||||
return "n"
|
||||
case reflect.Uint16:
|
||||
return "q"
|
||||
case reflect.Int32:
|
||||
if t == unixFDType {
|
||||
return "h"
|
||||
}
|
||||
return "i"
|
||||
case reflect.Uint32:
|
||||
if t == unixFDIndexType {
|
||||
return "h"
|
||||
}
|
||||
return "u"
|
||||
case reflect.Int64:
|
||||
return "x"
|
||||
case reflect.Uint64:
|
||||
return "t"
|
||||
case reflect.Float64:
|
||||
return "d"
|
||||
case reflect.Ptr:
|
||||
return getSignature(t.Elem())
|
||||
case reflect.String:
|
||||
if t == objectPathType {
|
||||
return "o"
|
||||
}
|
||||
return "s"
|
||||
case reflect.Struct:
|
||||
if t == variantType {
|
||||
return "v"
|
||||
} else if t == signatureType {
|
||||
return "g"
|
||||
}
|
||||
var s string
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
||||
s += getSignature(t.Field(i).Type)
|
||||
}
|
||||
}
|
||||
return "(" + s + ")"
|
||||
case reflect.Array, reflect.Slice:
|
||||
return "a" + getSignature(t.Elem())
|
||||
case reflect.Map:
|
||||
if !isKeyType(t.Key()) {
|
||||
panic(InvalidTypeError{t})
|
||||
}
|
||||
return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
|
||||
}
|
||||
panic(InvalidTypeError{t})
|
||||
}
|
||||
|
||||
// ParseSignature returns the signature represented by this string, or a
|
||||
// SignatureError if the string is not a valid signature.
|
||||
func ParseSignature(s string) (sig Signature, err error) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
if len(s) > 255 {
|
||||
return Signature{""}, SignatureError{s, "too long"}
|
||||
}
|
||||
sig.str = s
|
||||
for err == nil && len(s) != 0 {
|
||||
err, s = validSingle(s, 0)
|
||||
}
|
||||
if err != nil {
|
||||
sig = Signature{""}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ParseSignatureMust behaves like ParseSignature, except that it panics if s
|
||||
// is not valid.
|
||||
func ParseSignatureMust(s string) Signature {
|
||||
sig, err := ParseSignature(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
// Empty retruns whether the signature is the empty signature.
|
||||
func (s Signature) Empty() bool {
|
||||
return s.str == ""
|
||||
}
|
||||
|
||||
// Single returns whether the signature represents a single, complete type.
|
||||
func (s Signature) Single() bool {
|
||||
err, r := validSingle(s.str, 0)
|
||||
return err != nil && r == ""
|
||||
}
|
||||
|
||||
// String returns the signature's string representation.
|
||||
func (s Signature) String() string {
|
||||
return s.str
|
||||
}
|
||||
|
||||
// A SignatureError indicates that a signature passed to a function or received
|
||||
// on a connection is not a valid signature.
|
||||
type SignatureError struct {
|
||||
Sig string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e SignatureError) Error() string {
|
||||
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
||||
}
|
||||
|
||||
// Try to read a single type from this string. If it was successfull, err is nil
|
||||
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
||||
// SignatureError and rem is "". depth is the current recursion depth which may
|
||||
// not be greater than 64 and should be given as 0 on the first call.
|
||||
func validSingle(s string, depth int) (err error, rem string) {
|
||||
if s == "" {
|
||||
return SignatureError{Sig: s, Reason: "empty signature"}, ""
|
||||
}
|
||||
if depth > 64 {
|
||||
return SignatureError{Sig: s, Reason: "container nesting too deep"}, ""
|
||||
}
|
||||
switch s[0] {
|
||||
case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h':
|
||||
return nil, s[1:]
|
||||
case 'a':
|
||||
if len(s) > 1 && s[1] == '{' {
|
||||
i := findMatching(s[1:], '{', '}')
|
||||
if i == -1 {
|
||||
return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
|
||||
}
|
||||
i++
|
||||
rem = s[i+1:]
|
||||
s = s[2:i]
|
||||
if err, _ = validSingle(s[:1], depth+1); err != nil {
|
||||
return err, ""
|
||||
}
|
||||
err, nr := validSingle(s[1:], depth+1)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
if nr != "" {
|
||||
return SignatureError{Sig: s, Reason: "too many types in dict"}, ""
|
||||
}
|
||||
return nil, rem
|
||||
}
|
||||
return validSingle(s[1:], depth+1)
|
||||
case '(':
|
||||
i := findMatching(s, '(', ')')
|
||||
if i == -1 {
|
||||
return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
|
||||
}
|
||||
rem = s[i+1:]
|
||||
s = s[1:i]
|
||||
for err == nil && s != "" {
|
||||
err, s = validSingle(s, depth+1)
|
||||
}
|
||||
if err != nil {
|
||||
rem = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
return SignatureError{Sig: s, Reason: "invalid type character"}, ""
|
||||
}
|
||||
|
||||
func findMatching(s string, left, right rune) int {
|
||||
n := 0
|
||||
for i, v := range s {
|
||||
if v == left {
|
||||
n++
|
||||
} else if v == right {
|
||||
n--
|
||||
}
|
||||
if n == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// typeFor returns the type of the given signature. It ignores any left over
|
||||
// characters and panics if s doesn't start with a valid type signature.
|
||||
func typeFor(s string) (t reflect.Type) {
|
||||
err, _ := validSingle(s, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if t, ok := sigToType[s[0]]; ok {
|
||||
return t
|
||||
}
|
||||
switch s[0] {
|
||||
case 'a':
|
||||
if s[1] == '{' {
|
||||
i := strings.LastIndex(s, "}")
|
||||
t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
|
||||
} else {
|
||||
t = reflect.SliceOf(typeFor(s[1:]))
|
||||
}
|
||||
case '(':
|
||||
t = interfacesType
|
||||
}
|
||||
return
|
||||
}
|
||||
70
third_party/github.com/guelfey/go.dbus/sig_test.go
vendored
Normal file
70
third_party/github.com/guelfey/go.dbus/sig_test.go
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var sigTests = []struct {
|
||||
vs []interface{}
|
||||
sig Signature
|
||||
}{
|
||||
{
|
||||
[]interface{}{new(int32)},
|
||||
Signature{"i"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(string)},
|
||||
Signature{"s"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(Signature)},
|
||||
Signature{"g"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new([]int16)},
|
||||
Signature{"an"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(int16), new(uint32)},
|
||||
Signature{"nu"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(map[byte]Variant)},
|
||||
Signature{"a{yv}"},
|
||||
},
|
||||
{
|
||||
[]interface{}{new(Variant), new([]map[int32]string)},
|
||||
Signature{"vaa{is}"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSig(t *testing.T) {
|
||||
for i, v := range sigTests {
|
||||
sig := SignatureOf(v.vs...)
|
||||
if sig != v.sig {
|
||||
t.Errorf("test %d: got %q, expected %q", i+1, sig.str, v.sig.str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var getSigTest = []interface{}{
|
||||
[]struct {
|
||||
b byte
|
||||
i int32
|
||||
t uint64
|
||||
s string
|
||||
}{},
|
||||
map[string]Variant{},
|
||||
}
|
||||
|
||||
func BenchmarkGetSignatureSimple(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
SignatureOf("", int32(0))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetSignatureLong(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
SignatureOf(getSigTest...)
|
||||
}
|
||||
}
|
||||
6
third_party/github.com/guelfey/go.dbus/transport_darwin.go
vendored
Normal file
6
third_party/github.com/guelfey/go.dbus/transport_darwin.go
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package dbus
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
_, err := t.Write([]byte{0})
|
||||
return err
|
||||
}
|
||||
35
third_party/github.com/guelfey/go.dbus/transport_generic.go
vendored
Normal file
35
third_party/github.com/guelfey/go.dbus/transport_generic.go
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type genericTransport struct {
|
||||
io.ReadWriteCloser
|
||||
}
|
||||
|
||||
func (t genericTransport) SendNullByte() error {
|
||||
_, err := t.Write([]byte{0})
|
||||
return err
|
||||
}
|
||||
|
||||
func (t genericTransport) SupportsUnixFDs() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t genericTransport) EnableUnixFDs() {}
|
||||
|
||||
func (t genericTransport) ReadMessage() (*Message, error) {
|
||||
return DecodeMessage(t)
|
||||
}
|
||||
|
||||
func (t genericTransport) SendMessage(msg *Message) error {
|
||||
for _, v := range msg.Body {
|
||||
if _, ok := v.(UnixFD); ok {
|
||||
return errors.New("dbus: unix fd passing not enabled")
|
||||
}
|
||||
}
|
||||
return msg.EncodeTo(t, binary.LittleEndian)
|
||||
}
|
||||
190
third_party/github.com/guelfey/go.dbus/transport_unix.go
vendored
Normal file
190
third_party/github.com/guelfey/go.dbus/transport_unix.go
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type oobReader struct {
|
||||
conn *net.UnixConn
|
||||
oob []byte
|
||||
buf [4096]byte
|
||||
}
|
||||
|
||||
func (o *oobReader) Read(b []byte) (n int, err error) {
|
||||
n, oobn, flags, _, err := o.conn.ReadMsgUnix(b, o.buf[:])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if flags&syscall.MSG_CTRUNC != 0 {
|
||||
return n, errors.New("dbus: control data truncated (too many fds received)")
|
||||
}
|
||||
o.oob = append(o.oob, o.buf[:oobn]...)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type unixTransport struct {
|
||||
*net.UnixConn
|
||||
hasUnixFDs bool
|
||||
}
|
||||
|
||||
func newUnixTransport(keys string) (transport, error) {
|
||||
var err error
|
||||
|
||||
t := new(unixTransport)
|
||||
abstract := getKey(keys, "abstract")
|
||||
path := getKey(keys, "path")
|
||||
switch {
|
||||
case abstract == "" && path == "":
|
||||
return nil, errors.New("dbus: invalid address (neither path nor abstract set)")
|
||||
case abstract != "" && path == "":
|
||||
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: "@" + abstract, Net: "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
case abstract == "" && path != "":
|
||||
t.UnixConn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: path, Net: "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
default:
|
||||
return nil, errors.New("dbus: invalid address (both path and abstract set)")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *unixTransport) EnableUnixFDs() {
|
||||
t.hasUnixFDs = true
|
||||
}
|
||||
|
||||
func (t *unixTransport) ReadMessage() (*Message, error) {
|
||||
var (
|
||||
blen, hlen uint32
|
||||
csheader [16]byte
|
||||
headers []header
|
||||
order binary.ByteOrder
|
||||
unixfds uint32
|
||||
)
|
||||
// To be sure that all bytes of out-of-band data are read, we use a special
|
||||
// reader that uses ReadUnix on the underlying connection instead of Read
|
||||
// and gathers the out-of-band data in a buffer.
|
||||
rd := &oobReader{conn: t.UnixConn}
|
||||
// read the first 16 bytes (the part of the header that has a constant size),
|
||||
// from which we can figure out the length of the rest of the message
|
||||
if _, err := io.ReadFull(rd, csheader[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch csheader[0] {
|
||||
case 'l':
|
||||
order = binary.LittleEndian
|
||||
case 'B':
|
||||
order = binary.BigEndian
|
||||
default:
|
||||
return nil, InvalidMessageError("invalid byte order")
|
||||
}
|
||||
// csheader[4:8] -> length of message body, csheader[12:16] -> length of
|
||||
// header fields (without alignment)
|
||||
binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen)
|
||||
binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen)
|
||||
if hlen%8 != 0 {
|
||||
hlen += 8 - (hlen % 8)
|
||||
}
|
||||
|
||||
// decode headers and look for unix fds
|
||||
headerdata := make([]byte, hlen+4)
|
||||
copy(headerdata, csheader[12:])
|
||||
if _, err := io.ReadFull(t, headerdata[4:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dec := newDecoder(bytes.NewBuffer(headerdata), order)
|
||||
dec.pos = 12
|
||||
vs, err := dec.Decode(Signature{"a(yv)"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Store(vs, &headers)
|
||||
for _, v := range headers {
|
||||
if v.Field == byte(FieldUnixFDs) {
|
||||
unixfds, _ = v.Variant.value.(uint32)
|
||||
}
|
||||
}
|
||||
all := make([]byte, 16+hlen+blen)
|
||||
copy(all, csheader[:])
|
||||
copy(all[16:], headerdata[4:])
|
||||
if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unixfds != 0 {
|
||||
if !t.hasUnixFDs {
|
||||
return nil, errors.New("dbus: got unix fds on unsupported transport")
|
||||
}
|
||||
// read the fds from the OOB data
|
||||
scms, err := syscall.ParseSocketControlMessage(rd.oob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
return nil, errors.New("dbus: received more than one socket control message")
|
||||
}
|
||||
fds, err := syscall.ParseUnixRights(&scms[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg, err := DecodeMessage(bytes.NewBuffer(all))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// substitute the values in the message body (which are indices for the
|
||||
// array receiver via OOB) with the actual values
|
||||
for i, v := range msg.Body {
|
||||
if j, ok := v.(UnixFDIndex); ok {
|
||||
if uint32(j) >= unixfds {
|
||||
return nil, InvalidMessageError("invalid index for unix fd")
|
||||
}
|
||||
msg.Body[i] = UnixFD(fds[j])
|
||||
}
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
return DecodeMessage(bytes.NewBuffer(all))
|
||||
}
|
||||
|
||||
func (t *unixTransport) SendMessage(msg *Message) error {
|
||||
fds := make([]int, 0)
|
||||
for i, v := range msg.Body {
|
||||
if fd, ok := v.(UnixFD); ok {
|
||||
msg.Body[i] = UnixFDIndex(len(fds))
|
||||
fds = append(fds, int(fd))
|
||||
}
|
||||
}
|
||||
if len(fds) != 0 {
|
||||
if !t.hasUnixFDs {
|
||||
return errors.New("dbus: unix fd passing not enabled")
|
||||
}
|
||||
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
|
||||
oob := syscall.UnixRights(fds...)
|
||||
buf := new(bytes.Buffer)
|
||||
msg.EncodeTo(buf, binary.LittleEndian)
|
||||
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != buf.Len() || oobn != len(oob) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
} else {
|
||||
if err := msg.EncodeTo(t, binary.LittleEndian); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *unixTransport) SupportsUnixFDs() bool {
|
||||
return true
|
||||
}
|
||||
49
third_party/github.com/guelfey/go.dbus/transport_unix_test.go
vendored
Normal file
49
third_party/github.com/guelfey/go.dbus/transport_unix_test.go
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testString = `This is a test!
|
||||
This text should be read from the file that is created by this test.`
|
||||
|
||||
type unixFDTest struct{}
|
||||
|
||||
func (t unixFDTest) Test(fd UnixFD) (string, *Error) {
|
||||
var b [4096]byte
|
||||
file := os.NewFile(uintptr(fd), "testfile")
|
||||
defer file.Close()
|
||||
n, err := file.Read(b[:])
|
||||
if err != nil {
|
||||
return "", &Error{"com.github.guelfey.test.Error", nil}
|
||||
}
|
||||
return string(b[:n]), nil
|
||||
}
|
||||
|
||||
func TestUnixFDs(t *testing.T) {
|
||||
conn, err := SessionBus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer w.Close()
|
||||
if _, err := w.Write([]byte(testString)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := conn.Names()[0]
|
||||
test := unixFDTest{}
|
||||
conn.Export(test, "/com/github/guelfey/test", "com.github.guelfey.test")
|
||||
var s string
|
||||
obj := conn.Object(name, "/com/github/guelfey/test")
|
||||
err = obj.Call("com.github.guelfey.test.Test", 0, UnixFD(r.Fd())).Store(&s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != testString {
|
||||
t.Fatal("got", s, "wanted", testString)
|
||||
}
|
||||
}
|
||||
22
third_party/github.com/guelfey/go.dbus/transport_unixcred.go
vendored
Normal file
22
third_party/github.com/guelfey/go.dbus/transport_unixcred.go
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build !darwin
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
ucred := &syscall.Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||
b := syscall.UnixCredentials(ucred)
|
||||
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oobn != len(b) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
129
third_party/github.com/guelfey/go.dbus/variant.go
vendored
Normal file
129
third_party/github.com/guelfey/go.dbus/variant.go
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Variant represents the D-Bus variant type.
|
||||
type Variant struct {
|
||||
sig Signature
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// MakeVariant converts the given value to a Variant. It panics if v cannot be
|
||||
// represented as a D-Bus type.
|
||||
func MakeVariant(v interface{}) Variant {
|
||||
return Variant{SignatureOf(v), v}
|
||||
}
|
||||
|
||||
// ParseVariant parses the given string as a variant as described at
|
||||
// https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not
|
||||
// empty, it is taken to be the expected signature for the variant.
|
||||
func ParseVariant(s string, sig Signature) (Variant, error) {
|
||||
tokens := varLex(s)
|
||||
p := &varParser{tokens: tokens}
|
||||
n, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
if sig.str == "" {
|
||||
sig, err = varInfer(n)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
}
|
||||
v, err := n.Value(sig)
|
||||
if err != nil {
|
||||
return Variant{}, err
|
||||
}
|
||||
return MakeVariant(v), nil
|
||||
}
|
||||
|
||||
// format returns a formatted version of v and whether this string can be parsed
|
||||
// unambigously.
|
||||
func (v Variant) format() (string, bool) {
|
||||
switch v.sig.str[0] {
|
||||
case 'b', 'i':
|
||||
return fmt.Sprint(v.value), true
|
||||
case 'n', 'q', 'u', 'x', 't', 'd', 'h':
|
||||
return fmt.Sprint(v.value), false
|
||||
case 's':
|
||||
return strconv.Quote(v.value.(string)), true
|
||||
case 'o':
|
||||
return strconv.Quote(string(v.value.(ObjectPath))), false
|
||||
case 'g':
|
||||
return strconv.Quote(v.value.(Signature).str), false
|
||||
case 'v':
|
||||
s, unamb := v.value.(Variant).format()
|
||||
if !unamb {
|
||||
return "<@" + v.value.(Variant).sig.str + " " + s + ">", true
|
||||
}
|
||||
return "<" + s + ">", true
|
||||
case 'y':
|
||||
return fmt.Sprintf("%#x", v.value.(byte)), false
|
||||
}
|
||||
rv := reflect.ValueOf(v.value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice:
|
||||
if rv.Len() == 0 {
|
||||
return "[]", false
|
||||
}
|
||||
unamb := true
|
||||
buf := bytes.NewBuffer([]byte("["))
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
// TODO: slooow
|
||||
s, b := MakeVariant(rv.Index(i).Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
if i != rv.Len()-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String(), unamb
|
||||
case reflect.Map:
|
||||
if rv.Len() == 0 {
|
||||
return "{}", false
|
||||
}
|
||||
unamb := true
|
||||
buf := bytes.NewBuffer([]byte("{"))
|
||||
for i, k := range rv.MapKeys() {
|
||||
s, b := MakeVariant(k.Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
buf.WriteString(": ")
|
||||
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
if i != rv.Len()-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.String(), unamb
|
||||
}
|
||||
return `"INVALID"`, true
|
||||
}
|
||||
|
||||
// Signature returns the D-Bus signature of the underlying value of v.
|
||||
func (v Variant) Signature() Signature {
|
||||
return v.sig
|
||||
}
|
||||
|
||||
// String returns the string representation of the underlying value of v as
|
||||
// described at https://developer.gnome.org/glib/unstable/gvariant-text.html.
|
||||
func (v Variant) String() string {
|
||||
s, unamb := v.format()
|
||||
if !unamb {
|
||||
return "@" + v.sig.str + " " + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Value returns the underlying value of v.
|
||||
func (v Variant) Value() interface{} {
|
||||
return v.value
|
||||
}
|
||||
284
third_party/github.com/guelfey/go.dbus/variant_lexer.go
vendored
Normal file
284
third_party/github.com/guelfey/go.dbus/variant_lexer.go
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Heavily inspired by the lexer from text/template.
|
||||
|
||||
type varToken struct {
|
||||
typ varTokenType
|
||||
val string
|
||||
}
|
||||
|
||||
type varTokenType byte
|
||||
|
||||
const (
|
||||
tokEOF varTokenType = iota
|
||||
tokError
|
||||
tokNumber
|
||||
tokString
|
||||
tokBool
|
||||
tokArrayStart
|
||||
tokArrayEnd
|
||||
tokDictStart
|
||||
tokDictEnd
|
||||
tokVariantStart
|
||||
tokVariantEnd
|
||||
tokComma
|
||||
tokColon
|
||||
tokType
|
||||
tokByteString
|
||||
)
|
||||
|
||||
type varLexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
tokens []varToken
|
||||
}
|
||||
|
||||
type lexState func(*varLexer) lexState
|
||||
|
||||
func varLex(s string) []varToken {
|
||||
l := &varLexer{input: s}
|
||||
l.run()
|
||||
return l.tokens
|
||||
}
|
||||
|
||||
func (l *varLexer) accept(valid string) bool {
|
||||
if strings.IndexRune(valid, l.next()) >= 0 {
|
||||
return true
|
||||
}
|
||||
l.backup()
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *varLexer) backup() {
|
||||
l.pos -= l.width
|
||||
}
|
||||
|
||||
func (l *varLexer) emit(t varTokenType) {
|
||||
l.tokens = append(l.tokens, varToken{t, l.input[l.start:l.pos]})
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *varLexer) errorf(format string, v ...interface{}) lexState {
|
||||
l.tokens = append(l.tokens, varToken{
|
||||
tokError,
|
||||
fmt.Sprintf(format, v...),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *varLexer) ignore() {
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *varLexer) next() rune {
|
||||
var r rune
|
||||
|
||||
if l.pos >= len(l.input) {
|
||||
l.width = 0
|
||||
return -1
|
||||
}
|
||||
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||
l.pos += l.width
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *varLexer) run() {
|
||||
for state := varLexNormal; state != nil; {
|
||||
state = state(l)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *varLexer) peek() rune {
|
||||
r := l.next()
|
||||
l.backup()
|
||||
return r
|
||||
}
|
||||
|
||||
func varLexNormal(l *varLexer) lexState {
|
||||
for {
|
||||
r := l.next()
|
||||
switch {
|
||||
case r == -1:
|
||||
l.emit(tokEOF)
|
||||
return nil
|
||||
case r == '[':
|
||||
l.emit(tokArrayStart)
|
||||
case r == ']':
|
||||
l.emit(tokArrayEnd)
|
||||
case r == '{':
|
||||
l.emit(tokDictStart)
|
||||
case r == '}':
|
||||
l.emit(tokDictEnd)
|
||||
case r == '<':
|
||||
l.emit(tokVariantStart)
|
||||
case r == '>':
|
||||
l.emit(tokVariantEnd)
|
||||
case r == ':':
|
||||
l.emit(tokColon)
|
||||
case r == ',':
|
||||
l.emit(tokComma)
|
||||
case r == '\'' || r == '"':
|
||||
l.backup()
|
||||
return varLexString
|
||||
case r == '@':
|
||||
l.backup()
|
||||
return varLexType
|
||||
case unicode.IsSpace(r):
|
||||
l.ignore()
|
||||
case unicode.IsNumber(r) || r == '+' || r == '-':
|
||||
l.backup()
|
||||
return varLexNumber
|
||||
case r == 'b':
|
||||
pos := l.start
|
||||
if n := l.peek(); n == '"' || n == '\'' {
|
||||
return varLexByteString
|
||||
}
|
||||
// not a byte string; try to parse it as a type or bool below
|
||||
l.pos = pos + 1
|
||||
l.width = 1
|
||||
fallthrough
|
||||
default:
|
||||
// either a bool or a type. Try bools first.
|
||||
l.backup()
|
||||
if l.pos+4 <= len(l.input) {
|
||||
if l.input[l.pos:l.pos+4] == "true" {
|
||||
l.pos += 4
|
||||
l.emit(tokBool)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if l.pos+5 <= len(l.input) {
|
||||
if l.input[l.pos:l.pos+5] == "false" {
|
||||
l.pos += 5
|
||||
l.emit(tokBool)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// must be a type.
|
||||
return varLexType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var varTypeMap = map[string]string{
|
||||
"boolean": "b",
|
||||
"byte": "y",
|
||||
"int16": "n",
|
||||
"uint16": "q",
|
||||
"int32": "i",
|
||||
"uint32": "u",
|
||||
"int64": "x",
|
||||
"uint64": "t",
|
||||
"double": "f",
|
||||
"string": "s",
|
||||
"objectpath": "o",
|
||||
"signature": "g",
|
||||
}
|
||||
|
||||
func varLexByteString(l *varLexer) lexState {
|
||||
q := l.next()
|
||||
Loop:
|
||||
for {
|
||||
switch l.next() {
|
||||
case '\\':
|
||||
if r := l.next(); r != -1 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case -1:
|
||||
return l.errorf("unterminated bytestring")
|
||||
case q:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
l.emit(tokByteString)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexNumber(l *varLexer) lexState {
|
||||
l.accept("+-")
|
||||
digits := "0123456789"
|
||||
if l.accept("0") {
|
||||
if l.accept("x") {
|
||||
digits = "0123456789abcdefABCDEF"
|
||||
} else {
|
||||
digits = "01234567"
|
||||
}
|
||||
}
|
||||
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||
}
|
||||
l.backup()
|
||||
if l.accept(".") {
|
||||
for strings.IndexRune(digits, l.next()) >= 0 {
|
||||
}
|
||||
l.backup()
|
||||
}
|
||||
if l.accept("eE") {
|
||||
l.accept("+-")
|
||||
for strings.IndexRune("0123456789", l.next()) >= 0 {
|
||||
}
|
||||
l.backup()
|
||||
}
|
||||
if r := l.peek(); unicode.IsLetter(r) {
|
||||
l.next()
|
||||
return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
|
||||
}
|
||||
l.emit(tokNumber)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexString(l *varLexer) lexState {
|
||||
q := l.next()
|
||||
Loop:
|
||||
for {
|
||||
switch l.next() {
|
||||
case '\\':
|
||||
if r := l.next(); r != -1 {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case -1:
|
||||
return l.errorf("unterminated string")
|
||||
case q:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
l.emit(tokString)
|
||||
return varLexNormal
|
||||
}
|
||||
|
||||
func varLexType(l *varLexer) lexState {
|
||||
at := l.accept("@")
|
||||
for {
|
||||
r := l.next()
|
||||
if r == -1 {
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(r) {
|
||||
l.backup()
|
||||
break
|
||||
}
|
||||
}
|
||||
if at {
|
||||
if _, err := ParseSignature(l.input[l.start+1 : l.pos]); err != nil {
|
||||
return l.errorf("%s", err)
|
||||
}
|
||||
} else {
|
||||
if _, ok := varTypeMap[l.input[l.start:l.pos]]; ok {
|
||||
l.emit(tokType)
|
||||
return varLexNormal
|
||||
}
|
||||
return l.errorf("unrecognized type %q", l.input[l.start:l.pos])
|
||||
}
|
||||
l.emit(tokType)
|
||||
return varLexNormal
|
||||
}
|
||||
817
third_party/github.com/guelfey/go.dbus/variant_parser.go
vendored
Normal file
817
third_party/github.com/guelfey/go.dbus/variant_parser.go
vendored
Normal file
@@ -0,0 +1,817 @@
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type varParser struct {
|
||||
tokens []varToken
|
||||
i int
|
||||
}
|
||||
|
||||
func (p *varParser) backup() {
|
||||
p.i--
|
||||
}
|
||||
|
||||
func (p *varParser) next() varToken {
|
||||
if p.i < len(p.tokens) {
|
||||
t := p.tokens[p.i]
|
||||
p.i++
|
||||
return t
|
||||
}
|
||||
return varToken{typ: tokEOF}
|
||||
}
|
||||
|
||||
type varNode interface {
|
||||
Infer() (Signature, error)
|
||||
String() string
|
||||
Sigs() sigSet
|
||||
Value(Signature) (interface{}, error)
|
||||
}
|
||||
|
||||
func varMakeNode(p *varParser) (varNode, error) {
|
||||
var sig Signature
|
||||
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokNumber:
|
||||
return varMakeNumNode(t, sig)
|
||||
case tokString:
|
||||
return varMakeStringNode(t, sig)
|
||||
case tokBool:
|
||||
if sig.str != "" && sig.str != "b" {
|
||||
return nil, varTypeError{t.val, sig}
|
||||
}
|
||||
b, err := strconv.ParseBool(t.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return boolNode(b), nil
|
||||
case tokArrayStart:
|
||||
return varMakeArrayNode(p, sig)
|
||||
case tokVariantStart:
|
||||
return varMakeVariantNode(p, sig)
|
||||
case tokDictStart:
|
||||
return varMakeDictNode(p, sig)
|
||||
case tokType:
|
||||
if sig.str != "" {
|
||||
return nil, errors.New("unexpected type annotation")
|
||||
}
|
||||
if t.val[0] == '@' {
|
||||
sig.str = t.val[1:]
|
||||
} else {
|
||||
sig.str = varTypeMap[t.val]
|
||||
}
|
||||
case tokByteString:
|
||||
if sig.str != "" && sig.str != "ay" {
|
||||
return nil, varTypeError{t.val, sig}
|
||||
}
|
||||
b, err := varParseByteString(t.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return byteStringNode(b), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type varTypeError struct {
|
||||
val string
|
||||
sig Signature
|
||||
}
|
||||
|
||||
func (e varTypeError) Error() string {
|
||||
return fmt.Sprintf("dbus: can't parse %q as type %q", e.val, e.sig.str)
|
||||
}
|
||||
|
||||
type sigSet map[Signature]bool
|
||||
|
||||
func (s sigSet) Empty() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
func (s sigSet) Intersect(s2 sigSet) sigSet {
|
||||
r := make(sigSet)
|
||||
for k := range s {
|
||||
if s2[k] {
|
||||
r[k] = true
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (s sigSet) Single() (Signature, bool) {
|
||||
if len(s) == 1 {
|
||||
for k := range s {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
return Signature{}, false
|
||||
}
|
||||
|
||||
func (s sigSet) ToArray() sigSet {
|
||||
r := make(sigSet, len(s))
|
||||
for k := range s {
|
||||
r[Signature{"a" + k.str}] = true
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type numNode struct {
|
||||
sig Signature
|
||||
str string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
var numSigSet = sigSet{
|
||||
Signature{"y"}: true,
|
||||
Signature{"n"}: true,
|
||||
Signature{"q"}: true,
|
||||
Signature{"i"}: true,
|
||||
Signature{"u"}: true,
|
||||
Signature{"x"}: true,
|
||||
Signature{"t"}: true,
|
||||
Signature{"d"}: true,
|
||||
}
|
||||
|
||||
func (n numNode) Infer() (Signature, error) {
|
||||
if strings.ContainsAny(n.str, ".e") {
|
||||
return Signature{"d"}, nil
|
||||
}
|
||||
return Signature{"i"}, nil
|
||||
}
|
||||
|
||||
func (n numNode) String() string {
|
||||
return n.str
|
||||
}
|
||||
|
||||
func (n numNode) Sigs() sigSet {
|
||||
if n.sig.str != "" {
|
||||
return sigSet{n.sig: true}
|
||||
}
|
||||
if strings.ContainsAny(n.str, ".e") {
|
||||
return sigSet{Signature{"d"}: true}
|
||||
}
|
||||
return numSigSet
|
||||
}
|
||||
|
||||
func (n numNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.sig.str != "" && n.sig != sig {
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
if n.val != nil {
|
||||
return n.val, nil
|
||||
}
|
||||
return varNumAs(n.str, sig)
|
||||
}
|
||||
|
||||
func varMakeNumNode(tok varToken, sig Signature) (varNode, error) {
|
||||
if sig.str == "" {
|
||||
return numNode{str: tok.val}, nil
|
||||
}
|
||||
num, err := varNumAs(tok.val, sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return numNode{sig: sig, val: num}, nil
|
||||
}
|
||||
|
||||
func varNumAs(s string, sig Signature) (interface{}, error) {
|
||||
isUnsigned := false
|
||||
size := 32
|
||||
switch sig.str {
|
||||
case "n":
|
||||
size = 16
|
||||
case "i":
|
||||
case "x":
|
||||
size = 64
|
||||
case "y":
|
||||
size = 8
|
||||
isUnsigned = true
|
||||
case "q":
|
||||
size = 16
|
||||
isUnsigned = true
|
||||
case "u":
|
||||
isUnsigned = true
|
||||
case "t":
|
||||
size = 64
|
||||
isUnsigned = true
|
||||
case "d":
|
||||
d, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d, nil
|
||||
default:
|
||||
return nil, varTypeError{s, sig}
|
||||
}
|
||||
base := 10
|
||||
if strings.HasPrefix(s, "0x") {
|
||||
base = 16
|
||||
s = s[2:]
|
||||
}
|
||||
if strings.HasPrefix(s, "0") && len(s) != 1 {
|
||||
base = 8
|
||||
s = s[1:]
|
||||
}
|
||||
if isUnsigned {
|
||||
i, err := strconv.ParseUint(s, base, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v interface{} = i
|
||||
switch sig.str {
|
||||
case "y":
|
||||
v = byte(i)
|
||||
case "q":
|
||||
v = uint16(i)
|
||||
case "u":
|
||||
v = uint32(i)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
i, err := strconv.ParseInt(s, base, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v interface{} = i
|
||||
switch sig.str {
|
||||
case "n":
|
||||
v = int16(i)
|
||||
case "i":
|
||||
v = int32(i)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type stringNode struct {
|
||||
sig Signature
|
||||
str string // parsed
|
||||
val interface{} // has correct type
|
||||
}
|
||||
|
||||
var stringSigSet = sigSet{
|
||||
Signature{"s"}: true,
|
||||
Signature{"g"}: true,
|
||||
Signature{"o"}: true,
|
||||
}
|
||||
|
||||
func (n stringNode) Infer() (Signature, error) {
|
||||
return Signature{"s"}, nil
|
||||
}
|
||||
|
||||
func (n stringNode) String() string {
|
||||
return n.str
|
||||
}
|
||||
|
||||
func (n stringNode) Sigs() sigSet {
|
||||
if n.sig.str != "" {
|
||||
return sigSet{n.sig: true}
|
||||
}
|
||||
return stringSigSet
|
||||
}
|
||||
|
||||
func (n stringNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.sig.str != "" && n.sig != sig {
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
if n.val != nil {
|
||||
return n.val, nil
|
||||
}
|
||||
switch {
|
||||
case sig.str == "g":
|
||||
return Signature{n.str}, nil
|
||||
case sig.str == "o":
|
||||
return ObjectPath(n.str), nil
|
||||
case sig.str == "s":
|
||||
return n.str, nil
|
||||
default:
|
||||
return nil, varTypeError{n.str, sig}
|
||||
}
|
||||
}
|
||||
|
||||
func varMakeStringNode(tok varToken, sig Signature) (varNode, error) {
|
||||
if sig.str != "" && sig.str != "s" && sig.str != "g" && sig.str != "o" {
|
||||
return nil, fmt.Errorf("invalid type %q for string", sig.str)
|
||||
}
|
||||
s, err := varParseString(tok.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := stringNode{str: s}
|
||||
if sig.str == "" {
|
||||
return stringNode{str: s}, nil
|
||||
}
|
||||
n.sig = sig
|
||||
switch sig.str {
|
||||
case "o":
|
||||
n.val = ObjectPath(s)
|
||||
case "g":
|
||||
n.val = Signature{s}
|
||||
case "s":
|
||||
n.val = s
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func varParseString(s string) (string, error) {
|
||||
// quotes are guaranteed to be there
|
||||
s = s[1 : len(s)-1]
|
||||
buf := new(bytes.Buffer)
|
||||
for len(s) != 0 {
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
return "", errors.New("invalid UTF-8")
|
||||
}
|
||||
s = s[size:]
|
||||
if r != '\\' {
|
||||
buf.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
r, size = utf8.DecodeRuneInString(s)
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
return "", errors.New("invalid UTF-8")
|
||||
}
|
||||
s = s[size:]
|
||||
switch r {
|
||||
case 'a':
|
||||
buf.WriteRune(0x7)
|
||||
case 'b':
|
||||
buf.WriteRune(0x8)
|
||||
case 'f':
|
||||
buf.WriteRune(0xc)
|
||||
case 'n':
|
||||
buf.WriteRune('\n')
|
||||
case 'r':
|
||||
buf.WriteRune('\r')
|
||||
case 't':
|
||||
buf.WriteRune('\t')
|
||||
case '\n':
|
||||
case 'u':
|
||||
if len(s) < 4 {
|
||||
return "", errors.New("short unicode escape")
|
||||
}
|
||||
r, err := strconv.ParseUint(s[:4], 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteRune(rune(r))
|
||||
s = s[4:]
|
||||
case 'U':
|
||||
if len(s) < 8 {
|
||||
return "", errors.New("short unicode escape")
|
||||
}
|
||||
r, err := strconv.ParseUint(s[:8], 16, 32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteRune(rune(r))
|
||||
s = s[8:]
|
||||
default:
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
var boolSigSet = sigSet{Signature{"b"}: true}
|
||||
|
||||
type boolNode bool
|
||||
|
||||
func (boolNode) Infer() (Signature, error) {
|
||||
return Signature{"b"}, nil
|
||||
}
|
||||
|
||||
func (b boolNode) String() string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (boolNode) Sigs() sigSet {
|
||||
return boolSigSet
|
||||
}
|
||||
|
||||
func (b boolNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "b" {
|
||||
return nil, varTypeError{b.String(), sig}
|
||||
}
|
||||
return bool(b), nil
|
||||
}
|
||||
|
||||
type arrayNode struct {
|
||||
set sigSet
|
||||
children []varNode
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (n arrayNode) Infer() (Signature, error) {
|
||||
for _, v := range n.children {
|
||||
csig, err := varInfer(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return Signature{"a" + csig.str}, nil
|
||||
}
|
||||
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||
}
|
||||
|
||||
func (n arrayNode) String() string {
|
||||
s := "["
|
||||
for i, v := range n.children {
|
||||
s += v.String()
|
||||
if i != len(n.children)-1 {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
return s + "]"
|
||||
}
|
||||
|
||||
func (n arrayNode) Sigs() sigSet {
|
||||
return n.set
|
||||
}
|
||||
|
||||
func (n arrayNode) Value(sig Signature) (interface{}, error) {
|
||||
if n.set.Empty() {
|
||||
// no type information whatsoever, so this must be an empty slice
|
||||
return reflect.MakeSlice(typeFor(sig.str), 0, 0).Interface(), nil
|
||||
}
|
||||
if !n.set[sig] {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
s := reflect.MakeSlice(typeFor(sig.str), len(n.children), len(n.children))
|
||||
for i, v := range n.children {
|
||||
rv, err := v.Value(Signature{sig.str[1:]})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Index(i).Set(reflect.ValueOf(rv))
|
||||
}
|
||||
return s.Interface(), nil
|
||||
}
|
||||
|
||||
func varMakeArrayNode(p *varParser, sig Signature) (varNode, error) {
|
||||
var n arrayNode
|
||||
if sig.str != "" {
|
||||
n.set = sigSet{sig: true}
|
||||
}
|
||||
if t := p.next(); t.typ == tokArrayEnd {
|
||||
return n, nil
|
||||
} else {
|
||||
p.backup()
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
cn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cset := cn.Sigs(); !cset.Empty() {
|
||||
if n.set.Empty() {
|
||||
n.set = cset.ToArray()
|
||||
} else {
|
||||
nset := cset.ToArray().Intersect(n.set)
|
||||
if nset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", cn.String())
|
||||
}
|
||||
n.set = nset
|
||||
}
|
||||
}
|
||||
n.children = append(n.children, cn)
|
||||
switch t := p.next(); t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokArrayEnd:
|
||||
break Loop
|
||||
case tokComma:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type variantNode struct {
|
||||
n varNode
|
||||
}
|
||||
|
||||
var variantSet = sigSet{
|
||||
Signature{"v"}: true,
|
||||
}
|
||||
|
||||
func (variantNode) Infer() (Signature, error) {
|
||||
return Signature{"v"}, nil
|
||||
}
|
||||
|
||||
func (n variantNode) String() string {
|
||||
return "<" + n.n.String() + ">"
|
||||
}
|
||||
|
||||
func (variantNode) Sigs() sigSet {
|
||||
return variantSet
|
||||
}
|
||||
|
||||
func (n variantNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "v" {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
sig, err := varInfer(n.n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := n.n.Value(sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MakeVariant(v), nil
|
||||
}
|
||||
|
||||
func varMakeVariantNode(p *varParser, sig Signature) (varNode, error) {
|
||||
n, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t := p.next(); t.typ != tokVariantEnd {
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
vn := variantNode{n}
|
||||
if sig.str != "" && sig.str != "v" {
|
||||
return nil, varTypeError{vn.String(), sig}
|
||||
}
|
||||
return variantNode{n}, nil
|
||||
}
|
||||
|
||||
type dictEntry struct {
|
||||
key, val varNode
|
||||
}
|
||||
|
||||
type dictNode struct {
|
||||
kset, vset sigSet
|
||||
children []dictEntry
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (n dictNode) Infer() (Signature, error) {
|
||||
for _, v := range n.children {
|
||||
ksig, err := varInfer(v.key)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
vsig, err := varInfer(v.val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return Signature{"a{" + ksig.str + vsig.str + "}"}, nil
|
||||
}
|
||||
return Signature{}, fmt.Errorf("can't infer type for %q", n.String())
|
||||
}
|
||||
|
||||
func (n dictNode) String() string {
|
||||
s := "{"
|
||||
for i, v := range n.children {
|
||||
s += v.key.String() + ": " + v.val.String()
|
||||
if i != len(n.children)-1 {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
return s + "}"
|
||||
}
|
||||
|
||||
func (n dictNode) Sigs() sigSet {
|
||||
r := sigSet{}
|
||||
for k := range n.kset {
|
||||
for v := range n.vset {
|
||||
sig := "a{" + k.str + v.str + "}"
|
||||
r[Signature{sig}] = true
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (n dictNode) Value(sig Signature) (interface{}, error) {
|
||||
set := n.Sigs()
|
||||
if set.Empty() {
|
||||
// no type information -> empty dict
|
||||
return reflect.MakeMap(typeFor(sig.str)).Interface(), nil
|
||||
}
|
||||
if !set[sig] {
|
||||
return nil, varTypeError{n.String(), sig}
|
||||
}
|
||||
m := reflect.MakeMap(typeFor(sig.str))
|
||||
ksig := Signature{sig.str[2:3]}
|
||||
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||
for _, v := range n.children {
|
||||
kv, err := v.key.Value(ksig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vv, err := v.val.Value(vsig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
|
||||
}
|
||||
return m.Interface(), nil
|
||||
}
|
||||
|
||||
func varMakeDictNode(p *varParser, sig Signature) (varNode, error) {
|
||||
var n dictNode
|
||||
|
||||
if sig.str != "" {
|
||||
if len(sig.str) < 5 {
|
||||
return nil, fmt.Errorf("invalid signature %q for dict type", sig)
|
||||
}
|
||||
ksig := Signature{string(sig.str[2])}
|
||||
vsig := Signature{sig.str[3 : len(sig.str)-1]}
|
||||
n.kset = sigSet{ksig: true}
|
||||
n.vset = sigSet{vsig: true}
|
||||
}
|
||||
if t := p.next(); t.typ == tokDictEnd {
|
||||
return n, nil
|
||||
} else {
|
||||
p.backup()
|
||||
}
|
||||
Loop:
|
||||
for {
|
||||
t := p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
kn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if kset := kn.Sigs(); !kset.Empty() {
|
||||
if n.kset.Empty() {
|
||||
n.kset = kset
|
||||
} else {
|
||||
n.kset = kset.Intersect(n.kset)
|
||||
if n.kset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", kn.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokColon:
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
}
|
||||
p.backup()
|
||||
vn, err := varMakeNode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if vset := vn.Sigs(); !vset.Empty() {
|
||||
if n.vset.Empty() {
|
||||
n.vset = vset
|
||||
} else {
|
||||
n.vset = n.vset.Intersect(vset)
|
||||
if n.vset.Empty() {
|
||||
return nil, fmt.Errorf("can't parse %q with given type information", vn.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
n.children = append(n.children, dictEntry{kn, vn})
|
||||
t = p.next()
|
||||
switch t.typ {
|
||||
case tokEOF:
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
case tokError:
|
||||
return nil, errors.New(t.val)
|
||||
case tokDictEnd:
|
||||
break Loop
|
||||
case tokComma:
|
||||
continue
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %q", t.val)
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
type byteStringNode []byte
|
||||
|
||||
var byteStringSet = sigSet{
|
||||
Signature{"ay"}: true,
|
||||
}
|
||||
|
||||
func (byteStringNode) Infer() (Signature, error) {
|
||||
return Signature{"ay"}, nil
|
||||
}
|
||||
|
||||
func (b byteStringNode) String() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (b byteStringNode) Sigs() sigSet {
|
||||
return byteStringSet
|
||||
}
|
||||
|
||||
func (b byteStringNode) Value(sig Signature) (interface{}, error) {
|
||||
if sig.str != "ay" {
|
||||
return nil, varTypeError{b.String(), sig}
|
||||
}
|
||||
return []byte(b), nil
|
||||
}
|
||||
|
||||
func varParseByteString(s string) ([]byte, error) {
|
||||
// quotes and b at start are guaranteed to be there
|
||||
b := make([]byte, 0, 1)
|
||||
s = s[2 : len(s)-1]
|
||||
for len(s) != 0 {
|
||||
c := s[0]
|
||||
s = s[1:]
|
||||
if c != '\\' {
|
||||
b = append(b, c)
|
||||
continue
|
||||
}
|
||||
c = s[0]
|
||||
s = s[1:]
|
||||
switch c {
|
||||
case 'a':
|
||||
b = append(b, 0x7)
|
||||
case 'b':
|
||||
b = append(b, 0x8)
|
||||
case 'f':
|
||||
b = append(b, 0xc)
|
||||
case 'n':
|
||||
b = append(b, '\n')
|
||||
case 'r':
|
||||
b = append(b, '\r')
|
||||
case 't':
|
||||
b = append(b, '\t')
|
||||
case 'x':
|
||||
if len(s) < 2 {
|
||||
return nil, errors.New("short escape")
|
||||
}
|
||||
n, err := strconv.ParseUint(s[:2], 16, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, byte(n))
|
||||
s = s[2:]
|
||||
case '0':
|
||||
if len(s) < 3 {
|
||||
return nil, errors.New("short escape")
|
||||
}
|
||||
n, err := strconv.ParseUint(s[:3], 8, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, byte(n))
|
||||
s = s[3:]
|
||||
default:
|
||||
b = append(b, c)
|
||||
}
|
||||
}
|
||||
return append(b, 0), nil
|
||||
}
|
||||
|
||||
func varInfer(n varNode) (Signature, error) {
|
||||
if sig, ok := n.Sigs().Single(); ok {
|
||||
return sig, nil
|
||||
}
|
||||
return n.Infer()
|
||||
}
|
||||
78
third_party/github.com/guelfey/go.dbus/variant_test.go
vendored
Normal file
78
third_party/github.com/guelfey/go.dbus/variant_test.go
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package dbus
|
||||
|
||||
import "reflect"
|
||||
import "testing"
|
||||
|
||||
var variantFormatTests = []struct {
|
||||
v interface{}
|
||||
s string
|
||||
}{
|
||||
{int32(1), `1`},
|
||||
{"foo", `"foo"`},
|
||||
{ObjectPath("/org/foo"), `@o "/org/foo"`},
|
||||
{Signature{"i"}, `@g "i"`},
|
||||
{[]byte{}, `@ay []`},
|
||||
{[]int32{1, 2}, `[1, 2]`},
|
||||
{[]int64{1, 2}, `@ax [1, 2]`},
|
||||
{[][]int32{{3, 4}, {5, 6}}, `[[3, 4], [5, 6]]`},
|
||||
{[]Variant{MakeVariant(int32(1)), MakeVariant(1.0)}, `[<1>, <@d 1>]`},
|
||||
{map[string]int32{"one": 1, "two": 2}, `{"one": 1, "two": 2}`},
|
||||
{map[int32]ObjectPath{1: "/org/foo"}, `@a{io} {1: "/org/foo"}`},
|
||||
{map[string]Variant{}, `@a{sv} {}`},
|
||||
}
|
||||
|
||||
func TestFormatVariant(t *testing.T) {
|
||||
for i, v := range variantFormatTests {
|
||||
if s := MakeVariant(v.v).String(); s != v.s {
|
||||
t.Errorf("test %d: got %q, wanted %q", i+1, s, v.s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var variantParseTests = []struct {
|
||||
s string
|
||||
v interface{}
|
||||
}{
|
||||
{"1", int32(1)},
|
||||
{"true", true},
|
||||
{"false", false},
|
||||
{"1.0", float64(1.0)},
|
||||
{"0x10", int32(16)},
|
||||
{"1e1", float64(10)},
|
||||
{`"foo"`, "foo"},
|
||||
{`"\a\b\f\n\r\t"`, "\x07\x08\x0c\n\r\t"},
|
||||
{`"\u00e4\U0001f603"`, "\u00e4\U0001f603"},
|
||||
{"[1]", []int32{1}},
|
||||
{"[1, 2, 3]", []int32{1, 2, 3}},
|
||||
{"@ai []", []int32{}},
|
||||
{"[1, 5.0]", []float64{1, 5.0}},
|
||||
{"[[1, 2], [3, 4.0]]", [][]float64{{1, 2}, {3, 4}}},
|
||||
{`[@o "/org/foo", "/org/bar"]`, []ObjectPath{"/org/foo", "/org/bar"}},
|
||||
{"<1>", MakeVariant(int32(1))},
|
||||
{"[<1>, <2.0>]", []Variant{MakeVariant(int32(1)), MakeVariant(2.0)}},
|
||||
{`[[], [""]]`, [][]string{{}, {""}}},
|
||||
{`@a{ss} {}`, map[string]string{}},
|
||||
{`{"foo": 1}`, map[string]int32{"foo": 1}},
|
||||
{`[{}, {"foo": "bar"}]`, []map[string]string{{}, {"foo": "bar"}}},
|
||||
{`{"a": <1>, "b": <"foo">}`,
|
||||
map[string]Variant{"a": MakeVariant(int32(1)), "b": MakeVariant("foo")}},
|
||||
{`b''`, []byte{0}},
|
||||
{`b"abc"`, []byte{'a', 'b', 'c', 0}},
|
||||
{`b"\x01\0002\a\b\f\n\r\t"`, []byte{1, 2, 0x7, 0x8, 0xc, '\n', '\r', '\t', 0}},
|
||||
{`[[0], b""]`, [][]byte{{0}, {0}}},
|
||||
{"int16 0", int16(0)},
|
||||
{"byte 0", byte(0)},
|
||||
}
|
||||
|
||||
func TestParseVariant(t *testing.T) {
|
||||
for i, v := range variantParseTests {
|
||||
nv, err := ParseVariant(v.s, Signature{})
|
||||
if err != nil {
|
||||
t.Errorf("test %d: parsing failed: %s", i+1, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(nv.value, v.v) {
|
||||
t.Errorf("test %d: got %q, wanted %q", i+1, nv, v.v)
|
||||
}
|
||||
}
|
||||
}
|
||||
14
third_party/launchpad.net/goyaml/.bzrignore
vendored
Normal file
14
third_party/launchpad.net/goyaml/.bzrignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
[568].out
|
||||
_*
|
||||
*.cgo*.*
|
||||
|
||||
yaml-*/stamp-h1
|
||||
yaml-*/Makefile
|
||||
yaml-*/*/Makefile
|
||||
yaml-*/libtool
|
||||
yaml-*/config*
|
||||
yaml-*/*/*.lo
|
||||
yaml-*/*/*.la
|
||||
yaml-*/*/.libs
|
||||
yaml-*/*/.deps
|
||||
yaml-*/tests/*
|
||||
1
third_party/launchpad.net/goyaml/.lbox
vendored
Normal file
1
third_party/launchpad.net/goyaml/.lbox
vendored
Normal file
@@ -0,0 +1 @@
|
||||
propose -cr -for=lp:goyaml
|
||||
20
third_party/launchpad.net/goyaml/.lbox.check
vendored
Executable file
20
third_party/launchpad.net/goyaml/.lbox.check
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
BADFMT=`find * -name '*.go' | xargs gofmt -l`
|
||||
if [ -n "$BADFMT" ]; then
|
||||
BADFMT=`echo "$BADFMT" | sed "s/^/ /"`
|
||||
echo -e "gofmt is sad:\n\n$BADFMT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=`go version | awk '{print $3}'`
|
||||
if [ $VERSION == 'devel' ]; then
|
||||
go tool vet \
|
||||
-methods \
|
||||
-printf \
|
||||
-rangeloops \
|
||||
-printfuncs 'ErrorContextf:1,notFoundf:0,badReqErrorf:0,Commitf:0,Snapshotf:0,Debugf:0' \
|
||||
.
|
||||
fi
|
||||
185
third_party/launchpad.net/goyaml/LICENSE
vendored
Normal file
185
third_party/launchpad.net/goyaml/LICENSE
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
19
third_party/launchpad.net/goyaml/LICENSE.libyaml
vendored
Normal file
19
third_party/launchpad.net/goyaml/LICENSE.libyaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2006 Kirill Simonov
|
||||
|
||||
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.
|
||||
39
third_party/launchpad.net/goyaml/Makefile
vendored
Normal file
39
third_party/launchpad.net/goyaml/Makefile
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
YAML=yaml-0.1.3
|
||||
LIBYAML=$(PWD)/$(YAML)/src/.libs/libyaml.a
|
||||
|
||||
TARG=launchpad.net/goyaml
|
||||
|
||||
GOFILES=\
|
||||
goyaml.go\
|
||||
resolve.go\
|
||||
|
||||
CGOFILES=\
|
||||
decode.go\
|
||||
encode.go\
|
||||
|
||||
CGO_OFILES+=\
|
||||
helpers.o\
|
||||
api.o\
|
||||
scanner.o\
|
||||
reader.o\
|
||||
parser.o\
|
||||
writer.o\
|
||||
emitter.o\
|
||||
|
||||
GOFMT=gofmt
|
||||
|
||||
BADFMT:=$(shell $(GOFMT) -l $(GOFILES) $(CGOFILES) $(wildcard *_test.go))
|
||||
|
||||
all: package
|
||||
gofmt: $(BADFMT)
|
||||
@for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
|
||||
ifneq ($(BADFMT),)
|
||||
ifneq ($(MAKECMDGOALS),gofmt)
|
||||
$(warning WARNING: make gofmt: $(BADFMT))
|
||||
endif
|
||||
endif
|
||||
742
third_party/launchpad.net/goyaml/apic.go
vendored
Normal file
742
third_party/launchpad.net/goyaml/apic.go
vendored
Normal file
@@ -0,0 +1,742 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
|
||||
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
|
||||
|
||||
// Check if we can move the queue at the beginning of the buffer.
|
||||
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
|
||||
if parser.tokens_head != len(parser.tokens) {
|
||||
copy(parser.tokens, parser.tokens[parser.tokens_head:])
|
||||
}
|
||||
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
|
||||
parser.tokens_head = 0
|
||||
}
|
||||
parser.tokens = append(parser.tokens, *token)
|
||||
if pos < 0 {
|
||||
return
|
||||
}
|
||||
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
|
||||
parser.tokens[parser.tokens_head+pos] = *token
|
||||
}
|
||||
|
||||
// Create a new parser object.
|
||||
func yaml_parser_initialize(parser *yaml_parser_t) bool {
|
||||
*parser = yaml_parser_t{
|
||||
raw_buffer: make([]byte, 0, input_raw_buffer_size),
|
||||
buffer: make([]byte, 0, input_buffer_size),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy a parser object.
|
||||
func yaml_parser_delete(parser *yaml_parser_t) {
|
||||
*parser = yaml_parser_t{}
|
||||
}
|
||||
|
||||
// String read handler.
|
||||
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
if parser.input_pos == len(parser.input) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(buffer, parser.input[parser.input_pos:])
|
||||
parser.input_pos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// File read handler.
|
||||
func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
return parser.input_file.Read(buffer)
|
||||
}
|
||||
|
||||
// Set a string input.
|
||||
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_string_read_handler
|
||||
parser.input = input
|
||||
parser.input_pos = 0
|
||||
}
|
||||
|
||||
// Set a file input.
|
||||
func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_file_read_handler
|
||||
parser.input_file = file
|
||||
}
|
||||
|
||||
// Set the source encoding.
|
||||
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
||||
if parser.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the encoding only once")
|
||||
}
|
||||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
// Create a new emitter object.
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) bool {
|
||||
*emitter = yaml_emitter_t{
|
||||
buffer: make([]byte, output_buffer_size),
|
||||
raw_buffer: make([]byte, 0, output_raw_buffer_size),
|
||||
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy an emitter object.
|
||||
func yaml_emitter_delete(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{}
|
||||
}
|
||||
|
||||
// String write handler.
|
||||
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// File write handler.
|
||||
func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
_, err := emitter.output_file.Write(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a string output.
|
||||
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_string_write_handler
|
||||
emitter.output_buffer = output_buffer
|
||||
}
|
||||
|
||||
// Set a file output.
|
||||
func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_file_write_handler
|
||||
emitter.output_file = file
|
||||
}
|
||||
|
||||
// Set the output encoding.
|
||||
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
|
||||
if emitter.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the output encoding only once")
|
||||
}
|
||||
emitter.encoding = encoding
|
||||
}
|
||||
|
||||
// Set the canonical output style.
|
||||
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
|
||||
emitter.canonical = canonical
|
||||
}
|
||||
|
||||
//// Set the indentation increment.
|
||||
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
|
||||
if indent < 2 || indent > 9 {
|
||||
indent = 2
|
||||
}
|
||||
emitter.best_indent = indent
|
||||
}
|
||||
|
||||
// Set the preferred line width.
|
||||
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
|
||||
if width < 0 {
|
||||
width = -1
|
||||
}
|
||||
emitter.best_width = width
|
||||
}
|
||||
|
||||
// Set if unescaped non-ASCII characters are allowed.
|
||||
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
|
||||
emitter.unicode = unicode
|
||||
}
|
||||
|
||||
// Set the preferred line break character.
|
||||
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
|
||||
emitter.line_break = line_break
|
||||
}
|
||||
|
||||
///*
|
||||
// * Destroy a token object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_token_delete(yaml_token_t *token)
|
||||
//{
|
||||
// assert(token); // Non-NULL token object expected.
|
||||
//
|
||||
// switch (token.type)
|
||||
// {
|
||||
// case YAML_TAG_DIRECTIVE_TOKEN:
|
||||
// yaml_free(token.data.tag_directive.handle);
|
||||
// yaml_free(token.data.tag_directive.prefix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ALIAS_TOKEN:
|
||||
// yaml_free(token.data.alias.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ANCHOR_TOKEN:
|
||||
// yaml_free(token.data.anchor.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_TAG_TOKEN:
|
||||
// yaml_free(token.data.tag.handle);
|
||||
// yaml_free(token.data.tag.suffix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_SCALAR_TOKEN:
|
||||
// yaml_free(token.data.scalar.value);
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// memset(token, 0, sizeof(yaml_token_t));
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Check if a string is a valid UTF-8 sequence.
|
||||
// *
|
||||
// * Check 'reader.c' for more details on UTF-8 encoding.
|
||||
// */
|
||||
//
|
||||
//static int
|
||||
//yaml_check_utf8(yaml_char_t *start, size_t length)
|
||||
//{
|
||||
// yaml_char_t *end = start+length;
|
||||
// yaml_char_t *pointer = start;
|
||||
//
|
||||
// while (pointer < end) {
|
||||
// unsigned char octet;
|
||||
// unsigned int width;
|
||||
// unsigned int value;
|
||||
// size_t k;
|
||||
//
|
||||
// octet = pointer[0];
|
||||
// width = (octet & 0x80) == 0x00 ? 1 :
|
||||
// (octet & 0xE0) == 0xC0 ? 2 :
|
||||
// (octet & 0xF0) == 0xE0 ? 3 :
|
||||
// (octet & 0xF8) == 0xF0 ? 4 : 0;
|
||||
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
|
||||
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
|
||||
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
|
||||
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
|
||||
// if (!width) return 0;
|
||||
// if (pointer+width > end) return 0;
|
||||
// for (k = 1; k < width; k ++) {
|
||||
// octet = pointer[k];
|
||||
// if ((octet & 0xC0) != 0x80) return 0;
|
||||
// value = (value << 6) + (octet & 0x3F);
|
||||
// }
|
||||
// if (!((width == 1) ||
|
||||
// (width == 2 && value >= 0x80) ||
|
||||
// (width == 3 && value >= 0x800) ||
|
||||
// (width == 4 && value >= 0x10000))) return 0;
|
||||
//
|
||||
// pointer += width;
|
||||
// }
|
||||
//
|
||||
// return 1;
|
||||
//}
|
||||
//
|
||||
|
||||
// Create STREAM-START.
|
||||
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_START_EVENT,
|
||||
encoding: encoding,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create STREAM-END.
|
||||
func yaml_stream_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create DOCUMENT-START.
|
||||
func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t,
|
||||
tag_directives []yaml_tag_directive_t, implicit bool) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_START_EVENT,
|
||||
version_directive: version_directive,
|
||||
tag_directives: tag_directives,
|
||||
implicit: implicit,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create DOCUMENT-END.
|
||||
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_END_EVENT,
|
||||
implicit: implicit,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
///*
|
||||
// * Create ALIAS.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
|
||||
//{
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// anchor_copy *yaml_char_t = NULL
|
||||
//
|
||||
// assert(event) // Non-NULL event object is expected.
|
||||
// assert(anchor) // Non-NULL anchor is expected.
|
||||
//
|
||||
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
|
||||
//
|
||||
// anchor_copy = yaml_strdup(anchor)
|
||||
// if (!anchor_copy)
|
||||
// return 0
|
||||
//
|
||||
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
|
||||
// Create SCALAR.
|
||||
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SCALAR_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
value: value,
|
||||
implicit: plain_implicit,
|
||||
quoted_implicit: quoted_implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-START.
|
||||
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-END.
|
||||
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create MAPPING-START.
|
||||
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create MAPPING-END.
|
||||
func yaml_mapping_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy an event object.
|
||||
func yaml_event_delete(event *yaml_event_t) {
|
||||
*event = yaml_event_t{}
|
||||
}
|
||||
|
||||
///*
|
||||
// * Create a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_initialize(document *yaml_document_t,
|
||||
// version_directive *yaml_version_directive_t,
|
||||
// tag_directives_start *yaml_tag_directive_t,
|
||||
// tag_directives_end *yaml_tag_directive_t,
|
||||
// start_implicit int, end_implicit int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// struct {
|
||||
// start *yaml_node_t
|
||||
// end *yaml_node_t
|
||||
// top *yaml_node_t
|
||||
// } nodes = { NULL, NULL, NULL }
|
||||
// version_directive_copy *yaml_version_directive_t = NULL
|
||||
// struct {
|
||||
// start *yaml_tag_directive_t
|
||||
// end *yaml_tag_directive_t
|
||||
// top *yaml_tag_directive_t
|
||||
// } tag_directives_copy = { NULL, NULL, NULL }
|
||||
// value yaml_tag_directive_t = { NULL, NULL }
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert((tag_directives_start && tag_directives_end) ||
|
||||
// (tag_directives_start == tag_directives_end))
|
||||
// // Valid tag directives are expected.
|
||||
//
|
||||
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// if (version_directive) {
|
||||
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
|
||||
// if (!version_directive_copy) goto error
|
||||
// version_directive_copy.major = version_directive.major
|
||||
// version_directive_copy.minor = version_directive.minor
|
||||
// }
|
||||
//
|
||||
// if (tag_directives_start != tag_directives_end) {
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
|
||||
// goto error
|
||||
// for (tag_directive = tag_directives_start
|
||||
// tag_directive != tag_directives_end; tag_directive ++) {
|
||||
// assert(tag_directive.handle)
|
||||
// assert(tag_directive.prefix)
|
||||
// if (!yaml_check_utf8(tag_directive.handle,
|
||||
// strlen((char *)tag_directive.handle)))
|
||||
// goto error
|
||||
// if (!yaml_check_utf8(tag_directive.prefix,
|
||||
// strlen((char *)tag_directive.prefix)))
|
||||
// goto error
|
||||
// value.handle = yaml_strdup(tag_directive.handle)
|
||||
// value.prefix = yaml_strdup(tag_directive.prefix)
|
||||
// if (!value.handle || !value.prefix) goto error
|
||||
// if (!PUSH(&context, tag_directives_copy, value))
|
||||
// goto error
|
||||
// value.handle = NULL
|
||||
// value.prefix = NULL
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
|
||||
// tag_directives_copy.start, tag_directives_copy.top,
|
||||
// start_implicit, end_implicit, mark, mark)
|
||||
//
|
||||
// return 1
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, nodes)
|
||||
// yaml_free(version_directive_copy)
|
||||
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
|
||||
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
// }
|
||||
// STACK_DEL(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Destroy a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_document_delete(document *yaml_document_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
//
|
||||
// context.error = YAML_NO_ERROR // Eliminate a compliler warning.
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// while (!STACK_EMPTY(&context, document.nodes)) {
|
||||
// node yaml_node_t = POP(&context, document.nodes)
|
||||
// yaml_free(node.tag)
|
||||
// switch (node.type) {
|
||||
// case YAML_SCALAR_NODE:
|
||||
// yaml_free(node.data.scalar.value)
|
||||
// break
|
||||
// case YAML_SEQUENCE_NODE:
|
||||
// STACK_DEL(&context, node.data.sequence.items)
|
||||
// break
|
||||
// case YAML_MAPPING_NODE:
|
||||
// STACK_DEL(&context, node.data.mapping.pairs)
|
||||
// break
|
||||
// default:
|
||||
// assert(0) // Should not happen.
|
||||
// }
|
||||
// }
|
||||
// STACK_DEL(&context, document.nodes)
|
||||
//
|
||||
// yaml_free(document.version_directive)
|
||||
// for (tag_directive = document.tag_directives.start
|
||||
// tag_directive != document.tag_directives.end
|
||||
// tag_directive++) {
|
||||
// yaml_free(tag_directive.handle)
|
||||
// yaml_free(tag_directive.prefix)
|
||||
// }
|
||||
// yaml_free(document.tag_directives.start)
|
||||
//
|
||||
// memset(document, 0, sizeof(yaml_document_t))
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get a document node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_node(document *yaml_document_t, index int)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
|
||||
// return document.nodes.start + index - 1
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get the root object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_root_node(document *yaml_document_t)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (document.nodes.top != document.nodes.start) {
|
||||
// return document.nodes.start
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a scalar node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_scalar(document *yaml_document_t,
|
||||
// tag *yaml_char_t, value *yaml_char_t, length int,
|
||||
// style yaml_scalar_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// value_copy *yaml_char_t = NULL
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert(value) // Non-NULL value is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (length < 0) {
|
||||
// length = strlen((char *)value)
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(value, length)) goto error
|
||||
// value_copy = yaml_malloc(length+1)
|
||||
// if (!value_copy) goto error
|
||||
// memcpy(value_copy, value, length)
|
||||
// value_copy[length] = '\0'
|
||||
//
|
||||
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// yaml_free(tag_copy)
|
||||
// yaml_free(value_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a sequence node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_sequence(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_sequence_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_item_t
|
||||
// end *yaml_node_item_t
|
||||
// top *yaml_node_item_t
|
||||
// } items = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, items)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a mapping node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_mapping(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_mapping_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_pair_t
|
||||
// end *yaml_node_pair_t
|
||||
// top *yaml_node_pair_t
|
||||
// } pairs = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, pairs)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append an item to a sequence node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_sequence_item(document *yaml_document_t,
|
||||
// sequence int, item int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(sequence > 0
|
||||
// && document.nodes.start + sequence <= document.nodes.top)
|
||||
// // Valid sequence id is required.
|
||||
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
|
||||
// // A sequence node is required.
|
||||
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
|
||||
// // Valid item id is required.
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[sequence-1].data.sequence.items, item))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append a pair of a key and a value to a mapping node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_mapping_pair(document *yaml_document_t,
|
||||
// mapping int, key int, value int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// pair yaml_node_pair_t
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(mapping > 0
|
||||
// && document.nodes.start + mapping <= document.nodes.top)
|
||||
// // Valid mapping id is required.
|
||||
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
|
||||
// // A mapping node is required.
|
||||
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
|
||||
// // Valid key id is required.
|
||||
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
|
||||
// // Valid value id is required.
|
||||
//
|
||||
// pair.key = key
|
||||
// pair.value = value
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
//
|
||||
473
third_party/launchpad.net/goyaml/decode.go
vendored
Normal file
473
third_party/launchpad.net/goyaml/decode.go
vendored
Normal file
@@ -0,0 +1,473 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
documentNode = 1 << iota
|
||||
mappingNode
|
||||
sequenceNode
|
||||
scalarNode
|
||||
aliasNode
|
||||
)
|
||||
|
||||
type node struct {
|
||||
kind int
|
||||
line, column int
|
||||
tag string
|
||||
value string
|
||||
implicit bool
|
||||
children []*node
|
||||
anchors map[string]*node
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parser, produces a node tree out of a libyaml event stream.
|
||||
|
||||
type parser struct {
|
||||
parser yaml_parser_t
|
||||
event yaml_event_t
|
||||
doc *node
|
||||
}
|
||||
|
||||
func newParser(b []byte) *parser {
|
||||
p := parser{}
|
||||
if !yaml_parser_initialize(&p.parser) {
|
||||
panic("Failed to initialize YAML emitter")
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
b = []byte{'\n'}
|
||||
}
|
||||
|
||||
yaml_parser_set_input_string(&p.parser, b)
|
||||
|
||||
p.skip()
|
||||
if p.event.typ != yaml_STREAM_START_EVENT {
|
||||
panic("Expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
p.skip()
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *parser) destroy() {
|
||||
if p.event.typ != yaml_NO_EVENT {
|
||||
yaml_event_delete(&p.event)
|
||||
}
|
||||
yaml_parser_delete(&p.parser)
|
||||
}
|
||||
|
||||
func (p *parser) skip() {
|
||||
if p.event.typ != yaml_NO_EVENT {
|
||||
if p.event.typ == yaml_STREAM_END_EVENT {
|
||||
panic("Attempted to go past the end of stream. Corrupted value?")
|
||||
}
|
||||
yaml_event_delete(&p.event)
|
||||
}
|
||||
if !yaml_parser_parse(&p.parser, &p.event) {
|
||||
p.fail()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) fail() {
|
||||
var where string
|
||||
var line int
|
||||
if p.parser.problem_mark.line != 0 {
|
||||
line = p.parser.problem_mark.line
|
||||
} else if p.parser.context_mark.line != 0 {
|
||||
line = p.parser.context_mark.line
|
||||
}
|
||||
if line != 0 {
|
||||
where = "line " + strconv.Itoa(line) + ": "
|
||||
}
|
||||
var msg string
|
||||
if len(p.parser.problem) > 0 {
|
||||
msg = p.parser.problem
|
||||
} else {
|
||||
msg = "Unknown problem parsing YAML content"
|
||||
}
|
||||
panic(where + msg)
|
||||
}
|
||||
|
||||
func (p *parser) anchor(n *node, anchor []byte) {
|
||||
if anchor != nil {
|
||||
p.doc.anchors[string(anchor)] = n
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parse() *node {
|
||||
switch p.event.typ {
|
||||
case yaml_SCALAR_EVENT:
|
||||
return p.scalar()
|
||||
case yaml_ALIAS_EVENT:
|
||||
return p.alias()
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
return p.mapping()
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
return p.sequence()
|
||||
case yaml_DOCUMENT_START_EVENT:
|
||||
return p.document()
|
||||
case yaml_STREAM_END_EVENT:
|
||||
// Happens when attempting to decode an empty buffer.
|
||||
return nil
|
||||
default:
|
||||
panic("Attempted to parse unknown event: " +
|
||||
strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
panic("Unreachable")
|
||||
}
|
||||
|
||||
func (p *parser) node(kind int) *node {
|
||||
return &node{
|
||||
kind: kind,
|
||||
line: p.event.start_mark.line,
|
||||
column: p.event.start_mark.column,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) document() *node {
|
||||
n := p.node(documentNode)
|
||||
n.anchors = make(map[string]*node)
|
||||
p.doc = n
|
||||
p.skip()
|
||||
n.children = append(n.children, p.parse())
|
||||
if p.event.typ != yaml_DOCUMENT_END_EVENT {
|
||||
panic("Expected end of document event but got " +
|
||||
strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) alias() *node {
|
||||
n := p.node(aliasNode)
|
||||
n.value = string(p.event.anchor)
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) scalar() *node {
|
||||
n := p.node(scalarNode)
|
||||
n.value = string(p.event.value)
|
||||
n.tag = string(p.event.tag)
|
||||
n.implicit = p.event.implicit
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) sequence() *node {
|
||||
n := p.node(sequenceNode)
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
for p.event.typ != yaml_SEQUENCE_END_EVENT {
|
||||
n.children = append(n.children, p.parse())
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) mapping() *node {
|
||||
n := p.node(mappingNode)
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
for p.event.typ != yaml_MAPPING_END_EVENT {
|
||||
n.children = append(n.children, p.parse(), p.parse())
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Decoder, unmarshals a node into a provided value.
|
||||
|
||||
type decoder struct {
|
||||
doc *node
|
||||
aliases map[string]bool
|
||||
}
|
||||
|
||||
func newDecoder() *decoder {
|
||||
d := &decoder{}
|
||||
d.aliases = make(map[string]bool)
|
||||
return d
|
||||
}
|
||||
|
||||
// d.setter deals with setters and pointer dereferencing and initialization.
|
||||
//
|
||||
// It's a slightly convoluted case to handle properly:
|
||||
//
|
||||
// - nil pointers should be initialized, unless being set to nil
|
||||
// - we don't know at this point yet what's the value to SetYAML() with.
|
||||
// - we can't separate pointer deref/init and setter checking, because
|
||||
// a setter may be found while going down a pointer chain.
|
||||
//
|
||||
// Thus, here is how it takes care of it:
|
||||
//
|
||||
// - out is provided as a pointer, so that it can be replaced.
|
||||
// - when looking at a non-setter ptr, *out=ptr.Elem(), unless tag=!!null
|
||||
// - when a setter is found, *out=interface{}, and a set() function is
|
||||
// returned to call SetYAML() with the value of *out once it's defined.
|
||||
//
|
||||
func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) {
|
||||
again := true
|
||||
for again {
|
||||
again = false
|
||||
setter, _ := (*out).Interface().(Setter)
|
||||
if tag != "!!null" || setter != nil {
|
||||
if pv := (*out); pv.Kind() == reflect.Ptr {
|
||||
if pv.IsNil() {
|
||||
*out = reflect.New(pv.Type().Elem()).Elem()
|
||||
pv.Set((*out).Addr())
|
||||
} else {
|
||||
*out = pv.Elem()
|
||||
}
|
||||
setter, _ = pv.Interface().(Setter)
|
||||
again = true
|
||||
}
|
||||
}
|
||||
if setter != nil {
|
||||
var arg interface{}
|
||||
*out = reflect.ValueOf(&arg).Elem()
|
||||
return func() {
|
||||
*good = setter.SetYAML(tag, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
|
||||
switch n.kind {
|
||||
case documentNode:
|
||||
good = d.document(n, out)
|
||||
case scalarNode:
|
||||
good = d.scalar(n, out)
|
||||
case aliasNode:
|
||||
good = d.alias(n, out)
|
||||
case mappingNode:
|
||||
good = d.mapping(n, out)
|
||||
case sequenceNode:
|
||||
good = d.sequence(n, out)
|
||||
default:
|
||||
panic("Internal error: unknown node kind: " + strconv.Itoa(n.kind))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
|
||||
if len(n.children) == 1 {
|
||||
d.doc = n
|
||||
d.unmarshal(n.children[0], out)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
|
||||
an, ok := d.doc.anchors[n.value]
|
||||
if !ok {
|
||||
panic("Unknown anchor '" + n.value + "' referenced")
|
||||
}
|
||||
if d.aliases[n.value] {
|
||||
panic("Anchor '" + n.value + "' value contains itself")
|
||||
}
|
||||
d.aliases[n.value] = true
|
||||
good = d.unmarshal(an, out)
|
||||
delete(d.aliases, n.value)
|
||||
return good
|
||||
}
|
||||
|
||||
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
|
||||
var tag string
|
||||
var resolved interface{}
|
||||
if n.tag == "" && !n.implicit {
|
||||
resolved = n.value
|
||||
} else {
|
||||
tag, resolved = resolve(n.tag, n.value)
|
||||
if set := d.setter(tag, &out, &good); set != nil {
|
||||
defer set()
|
||||
}
|
||||
}
|
||||
switch out.Kind() {
|
||||
case reflect.String:
|
||||
if resolved != nil {
|
||||
out.SetString(n.value)
|
||||
good = true
|
||||
}
|
||||
case reflect.Interface:
|
||||
if resolved == nil {
|
||||
out.Set(reflect.Zero(out.Type()))
|
||||
} else {
|
||||
out.Set(reflect.ValueOf(resolved))
|
||||
}
|
||||
good = true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
if !out.OverflowInt(int64(resolved)) {
|
||||
out.SetInt(int64(resolved))
|
||||
good = true
|
||||
}
|
||||
case int64:
|
||||
if !out.OverflowInt(resolved) {
|
||||
out.SetInt(resolved)
|
||||
good = true
|
||||
}
|
||||
case float64:
|
||||
if resolved < 1<<63-1 && !out.OverflowInt(int64(resolved)) {
|
||||
out.SetInt(int64(resolved))
|
||||
good = true
|
||||
}
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
if resolved >= 0 {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
case int64:
|
||||
if resolved >= 0 {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
case float64:
|
||||
if resolved < 1<<64-1 && !out.OverflowUint(uint64(resolved)) {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
}
|
||||
case reflect.Bool:
|
||||
switch resolved := resolved.(type) {
|
||||
case bool:
|
||||
out.SetBool(resolved)
|
||||
good = true
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
out.SetFloat(float64(resolved))
|
||||
good = true
|
||||
case int64:
|
||||
out.SetFloat(float64(resolved))
|
||||
good = true
|
||||
case float64:
|
||||
out.SetFloat(resolved)
|
||||
good = true
|
||||
}
|
||||
case reflect.Ptr:
|
||||
switch resolved.(type) {
|
||||
case nil:
|
||||
out.Set(reflect.Zero(out.Type()))
|
||||
good = true
|
||||
default:
|
||||
if out.Type().Elem() == reflect.TypeOf(resolved) {
|
||||
elem := reflect.New(out.Type().Elem())
|
||||
elem.Elem().Set(reflect.ValueOf(resolved))
|
||||
out.Set(elem)
|
||||
good = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return good
|
||||
}
|
||||
|
||||
func settableValueOf(i interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(i)
|
||||
sv := reflect.New(v.Type()).Elem()
|
||||
sv.Set(v)
|
||||
return sv
|
||||
}
|
||||
|
||||
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
|
||||
if set := d.setter("!!seq", &out, &good); set != nil {
|
||||
defer set()
|
||||
}
|
||||
var iface reflect.Value
|
||||
if out.Kind() == reflect.Interface {
|
||||
// No type hints. Will have to use a generic sequence.
|
||||
iface = out
|
||||
out = settableValueOf(make([]interface{}, 0))
|
||||
}
|
||||
|
||||
if out.Kind() != reflect.Slice {
|
||||
return false
|
||||
}
|
||||
et := out.Type().Elem()
|
||||
|
||||
l := len(n.children)
|
||||
for i := 0; i < l; i++ {
|
||||
e := reflect.New(et).Elem()
|
||||
if ok := d.unmarshal(n.children[i], e); ok {
|
||||
out.Set(reflect.Append(out, e))
|
||||
}
|
||||
}
|
||||
if iface.IsValid() {
|
||||
iface.Set(out)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
|
||||
if set := d.setter("!!map", &out, &good); set != nil {
|
||||
defer set()
|
||||
}
|
||||
if out.Kind() == reflect.Struct {
|
||||
return d.mappingStruct(n, out)
|
||||
}
|
||||
|
||||
if out.Kind() == reflect.Interface {
|
||||
// No type hints. Will have to use a generic map.
|
||||
iface := out
|
||||
out = settableValueOf(make(map[interface{}]interface{}))
|
||||
iface.Set(out)
|
||||
}
|
||||
|
||||
if out.Kind() != reflect.Map {
|
||||
return false
|
||||
}
|
||||
outt := out.Type()
|
||||
kt := outt.Key()
|
||||
et := outt.Elem()
|
||||
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeMap(outt))
|
||||
}
|
||||
l := len(n.children)
|
||||
for i := 0; i < l; i += 2 {
|
||||
k := reflect.New(kt).Elem()
|
||||
if d.unmarshal(n.children[i], k) {
|
||||
e := reflect.New(et).Elem()
|
||||
if d.unmarshal(n.children[i+1], e) {
|
||||
out.SetMapIndex(k, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
|
||||
sinfo, err := getStructInfo(out.Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
name := settableValueOf("")
|
||||
l := len(n.children)
|
||||
for i := 0; i < l; i += 2 {
|
||||
if !d.unmarshal(n.children[i], name) {
|
||||
continue
|
||||
}
|
||||
if info, ok := sinfo.FieldsMap[name.String()]; ok {
|
||||
var field reflect.Value
|
||||
if info.Inline == nil {
|
||||
field = out.Field(info.Num)
|
||||
} else {
|
||||
field = out.FieldByIndex(info.Inline)
|
||||
}
|
||||
d.unmarshal(n.children[i+1], field)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
519
third_party/launchpad.net/goyaml/decode_test.go
vendored
Normal file
519
third_party/launchpad.net/goyaml/decode_test.go
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
package goyaml_test
|
||||
|
||||
import (
|
||||
. "launchpad.net/gocheck"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var unmarshalIntTest = 123
|
||||
|
||||
var unmarshalTests = []struct {
|
||||
data string
|
||||
value interface{}
|
||||
}{
|
||||
{
|
||||
"",
|
||||
&struct{}{},
|
||||
}, {
|
||||
"{}", &struct{}{},
|
||||
}, {
|
||||
"v: hi",
|
||||
map[string]string{"v": "hi"},
|
||||
}, {
|
||||
"v: hi", map[string]interface{}{"v": "hi"},
|
||||
}, {
|
||||
"v: true",
|
||||
map[string]string{"v": "true"},
|
||||
}, {
|
||||
"v: true",
|
||||
map[string]interface{}{"v": true},
|
||||
}, {
|
||||
"v: 10",
|
||||
map[string]interface{}{"v": 10},
|
||||
}, {
|
||||
"v: 0b10",
|
||||
map[string]interface{}{"v": 2},
|
||||
}, {
|
||||
"v: 0xA",
|
||||
map[string]interface{}{"v": 10},
|
||||
}, {
|
||||
"v: 4294967296",
|
||||
map[string]int64{"v": 4294967296},
|
||||
}, {
|
||||
"v: 0.1",
|
||||
map[string]interface{}{"v": 0.1},
|
||||
}, {
|
||||
"v: .1",
|
||||
map[string]interface{}{"v": 0.1},
|
||||
}, {
|
||||
"v: .Inf",
|
||||
map[string]interface{}{"v": math.Inf(+1)},
|
||||
}, {
|
||||
"v: -.Inf",
|
||||
map[string]interface{}{"v": math.Inf(-1)},
|
||||
}, {
|
||||
"v: -10",
|
||||
map[string]interface{}{"v": -10},
|
||||
}, {
|
||||
"v: -.1",
|
||||
map[string]interface{}{"v": -0.1},
|
||||
},
|
||||
|
||||
// Simple values.
|
||||
{
|
||||
"123",
|
||||
&unmarshalIntTest,
|
||||
},
|
||||
|
||||
// Floats from spec
|
||||
{
|
||||
"canonical: 6.8523e+5",
|
||||
map[string]interface{}{"canonical": 6.8523e+5},
|
||||
}, {
|
||||
"expo: 685.230_15e+03",
|
||||
map[string]interface{}{"expo": 685.23015e+03},
|
||||
}, {
|
||||
"fixed: 685_230.15",
|
||||
map[string]interface{}{"fixed": 685230.15},
|
||||
}, {
|
||||
"neginf: -.inf",
|
||||
map[string]interface{}{"neginf": math.Inf(-1)},
|
||||
}, {
|
||||
"fixed: 685_230.15",
|
||||
map[string]float64{"fixed": 685230.15},
|
||||
},
|
||||
//{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported
|
||||
//{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails.
|
||||
|
||||
// Bools from spec
|
||||
{
|
||||
"canonical: y",
|
||||
map[string]interface{}{"canonical": true},
|
||||
}, {
|
||||
"answer: NO",
|
||||
map[string]interface{}{"answer": false},
|
||||
}, {
|
||||
"logical: True",
|
||||
map[string]interface{}{"logical": true},
|
||||
}, {
|
||||
"option: on",
|
||||
map[string]interface{}{"option": true},
|
||||
}, {
|
||||
"option: on",
|
||||
map[string]bool{"option": true},
|
||||
},
|
||||
// Ints from spec
|
||||
{
|
||||
"canonical: 685230",
|
||||
map[string]interface{}{"canonical": 685230},
|
||||
}, {
|
||||
"decimal: +685_230",
|
||||
map[string]interface{}{"decimal": 685230},
|
||||
}, {
|
||||
"octal: 02472256",
|
||||
map[string]interface{}{"octal": 685230},
|
||||
}, {
|
||||
"hexa: 0x_0A_74_AE",
|
||||
map[string]interface{}{"hexa": 685230},
|
||||
}, {
|
||||
"bin: 0b1010_0111_0100_1010_1110",
|
||||
map[string]interface{}{"bin": 685230},
|
||||
}, {
|
||||
"bin: -0b101010",
|
||||
map[string]interface{}{"bin": -42},
|
||||
}, {
|
||||
"decimal: +685_230",
|
||||
map[string]int{"decimal": 685230},
|
||||
},
|
||||
|
||||
//{"sexa: 190:20:30", map[string]interface{}{"sexa": 0}}, // Unsupported
|
||||
|
||||
// Nulls from spec
|
||||
{
|
||||
"empty:",
|
||||
map[string]interface{}{"empty": nil},
|
||||
}, {
|
||||
"canonical: ~",
|
||||
map[string]interface{}{"canonical": nil},
|
||||
}, {
|
||||
"english: null",
|
||||
map[string]interface{}{"english": nil},
|
||||
}, {
|
||||
"~: null key",
|
||||
map[interface{}]string{nil: "null key"},
|
||||
}, {
|
||||
"empty:",
|
||||
map[string]*bool{"empty": nil},
|
||||
},
|
||||
|
||||
// Flow sequence
|
||||
{
|
||||
"seq: [A,B]",
|
||||
map[string]interface{}{"seq": []interface{}{"A", "B"}},
|
||||
}, {
|
||||
"seq: [A,B,C,]",
|
||||
map[string][]string{"seq": []string{"A", "B", "C"}},
|
||||
}, {
|
||||
"seq: [A,1,C]",
|
||||
map[string][]string{"seq": []string{"A", "1", "C"}},
|
||||
}, {
|
||||
"seq: [A,1,C]",
|
||||
map[string][]int{"seq": []int{1}},
|
||||
}, {
|
||||
"seq: [A,1,C]",
|
||||
map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
|
||||
},
|
||||
// Block sequence
|
||||
{
|
||||
"seq:\n - A\n - B",
|
||||
map[string]interface{}{"seq": []interface{}{"A", "B"}},
|
||||
}, {
|
||||
"seq:\n - A\n - B\n - C",
|
||||
map[string][]string{"seq": []string{"A", "B", "C"}},
|
||||
}, {
|
||||
"seq:\n - A\n - 1\n - C",
|
||||
map[string][]string{"seq": []string{"A", "1", "C"}},
|
||||
}, {
|
||||
"seq:\n - A\n - 1\n - C",
|
||||
map[string][]int{"seq": []int{1}},
|
||||
}, {
|
||||
"seq:\n - A\n - 1\n - C",
|
||||
map[string]interface{}{"seq": []interface{}{"A", 1, "C"}},
|
||||
},
|
||||
|
||||
// Literal block scalar
|
||||
{
|
||||
"scalar: | # Comment\n\n literal\n\n \ttext\n\n",
|
||||
map[string]string{"scalar": "\nliteral\n\n\ttext\n"},
|
||||
},
|
||||
|
||||
// Folded block scalar
|
||||
{
|
||||
"scalar: > # Comment\n\n folded\n line\n \n next\n line\n * one\n * two\n\n last\n line\n\n",
|
||||
map[string]string{"scalar": "\nfolded line\nnext line\n * one\n * two\n\nlast line\n"},
|
||||
},
|
||||
|
||||
// Map inside interface with no type hints.
|
||||
{
|
||||
"a: {b: c}",
|
||||
map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
|
||||
},
|
||||
|
||||
// Structs and type conversions.
|
||||
{
|
||||
"hello: world",
|
||||
&struct{ Hello string }{"world"},
|
||||
}, {
|
||||
"a: {b: c}",
|
||||
&struct{ A struct{ B string } }{struct{ B string }{"c"}},
|
||||
}, {
|
||||
"a: {b: c}",
|
||||
&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
|
||||
}, {
|
||||
"a: {b: c}",
|
||||
&struct{ A map[string]string }{map[string]string{"b": "c"}},
|
||||
}, {
|
||||
"a: {b: c}",
|
||||
&struct{ A *map[string]string }{&map[string]string{"b": "c"}},
|
||||
}, {
|
||||
"a:",
|
||||
&struct{ A map[string]string }{},
|
||||
}, {
|
||||
"a: 1",
|
||||
&struct{ A int }{1},
|
||||
}, {
|
||||
"a: 1",
|
||||
&struct{ A float64 }{1},
|
||||
}, {
|
||||
"a: 1.0",
|
||||
&struct{ A int }{1},
|
||||
}, {
|
||||
"a: 1.0",
|
||||
&struct{ A uint }{1},
|
||||
}, {
|
||||
"a: [1, 2]",
|
||||
&struct{ A []int }{[]int{1, 2}},
|
||||
}, {
|
||||
"a: 1",
|
||||
&struct{ B int }{0},
|
||||
}, {
|
||||
"a: 1",
|
||||
&struct {
|
||||
B int "a"
|
||||
}{1},
|
||||
}, {
|
||||
"a: y",
|
||||
&struct{ A bool }{true},
|
||||
},
|
||||
|
||||
// Some cross type conversions
|
||||
{
|
||||
"v: 42",
|
||||
map[string]uint{"v": 42},
|
||||
}, {
|
||||
"v: -42",
|
||||
map[string]uint{},
|
||||
}, {
|
||||
"v: 4294967296",
|
||||
map[string]uint64{"v": 4294967296},
|
||||
}, {
|
||||
"v: -4294967296",
|
||||
map[string]uint64{},
|
||||
},
|
||||
|
||||
// Overflow cases.
|
||||
{
|
||||
"v: 4294967297",
|
||||
map[string]int32{},
|
||||
}, {
|
||||
"v: 128",
|
||||
map[string]int8{},
|
||||
},
|
||||
|
||||
// Quoted values.
|
||||
{
|
||||
"'1': '\"2\"'",
|
||||
map[interface{}]interface{}{"1": "\"2\""},
|
||||
}, {
|
||||
"v:\n- A\n- 'B\n\n C'\n",
|
||||
map[string][]string{"v": []string{"A", "B\nC"}},
|
||||
},
|
||||
|
||||
// Explicit tags.
|
||||
{
|
||||
"v: !!float '1.1'",
|
||||
map[string]interface{}{"v": 1.1},
|
||||
}, {
|
||||
"v: !!null ''",
|
||||
map[string]interface{}{"v": nil},
|
||||
}, {
|
||||
"%TAG !y! tag:yaml.org,2002:\n---\nv: !y!int '1'",
|
||||
map[string]interface{}{"v": 1},
|
||||
},
|
||||
|
||||
// Anchors and aliases.
|
||||
{
|
||||
"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
|
||||
&struct{ A, B, C, D int }{1, 2, 1, 2},
|
||||
}, {
|
||||
"a: &a {c: 1}\nb: *a",
|
||||
&struct {
|
||||
A, B struct {
|
||||
C int
|
||||
}
|
||||
}{struct{ C int }{1}, struct{ C int }{1}},
|
||||
}, {
|
||||
"a: &a [1, 2]\nb: *a",
|
||||
&struct{ B []int }{[]int{1, 2}},
|
||||
},
|
||||
|
||||
// Bug #1133337
|
||||
{
|
||||
"foo: ''",
|
||||
map[string]*string{"foo": new(string)},
|
||||
}, {
|
||||
"foo: null",
|
||||
map[string]string{},
|
||||
},
|
||||
|
||||
// Ignored field
|
||||
{
|
||||
"a: 1\nb: 2\n",
|
||||
&struct {
|
||||
A int
|
||||
B int "-"
|
||||
}{1, 0},
|
||||
},
|
||||
|
||||
// Bug #1191981
|
||||
{
|
||||
"" +
|
||||
"%YAML 1.1\n" +
|
||||
"--- !!str\n" +
|
||||
`"Generic line break (no glyph)\n\` + "\n" +
|
||||
` Generic line break (glyphed)\n\` + "\n" +
|
||||
` Line separator\u2028\` + "\n" +
|
||||
` Paragraph separator\u2029"` + "\n",
|
||||
"" +
|
||||
"Generic line break (no glyph)\n" +
|
||||
"Generic line break (glyphed)\n" +
|
||||
"Line separator\u2028Paragraph separator\u2029",
|
||||
},
|
||||
|
||||
// Struct inlining
|
||||
{
|
||||
"a: 1\nb: 2\nc: 3\n",
|
||||
&struct {
|
||||
A int
|
||||
C inlineB `yaml:",inline"`
|
||||
}{1, inlineB{2, inlineC{3}}},
|
||||
},
|
||||
}
|
||||
|
||||
type inlineB struct {
|
||||
B int
|
||||
inlineC `yaml:",inline"`
|
||||
}
|
||||
|
||||
type inlineC struct {
|
||||
C int
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshal(c *C) {
|
||||
for i, item := range unmarshalTests {
|
||||
t := reflect.ValueOf(item.value).Type()
|
||||
var value interface{}
|
||||
switch t.Kind() {
|
||||
case reflect.Map:
|
||||
value = reflect.MakeMap(t).Interface()
|
||||
case reflect.String:
|
||||
t := reflect.ValueOf(item.value).Type()
|
||||
v := reflect.New(t)
|
||||
value = v.Interface()
|
||||
default:
|
||||
pt := reflect.ValueOf(item.value).Type()
|
||||
pv := reflect.New(pt.Elem())
|
||||
value = pv.Interface()
|
||||
}
|
||||
err := goyaml.Unmarshal([]byte(item.data), value)
|
||||
c.Assert(err, IsNil, Commentf("Item #%d", i))
|
||||
if t.Kind() == reflect.String {
|
||||
c.Assert(*value.(*string), Equals, item.value, Commentf("Item #%d", i))
|
||||
} else {
|
||||
c.Assert(value, DeepEquals, item.value, Commentf("Item #%d", i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalNaN(c *C) {
|
||||
value := map[string]interface{}{}
|
||||
err := goyaml.Unmarshal([]byte("notanum: .NaN"), &value)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true)
|
||||
}
|
||||
|
||||
var unmarshalErrorTests = []struct {
|
||||
data, error string
|
||||
}{
|
||||
{"v: !!float 'error'", "YAML error: Can't decode !!str 'error' as a !!float"},
|
||||
{"v: [A,", "YAML error: line 1: did not find expected node content"},
|
||||
{"v:\n- [A,", "YAML error: line 2: did not find expected node content"},
|
||||
{"a: *b\n", "YAML error: Unknown anchor 'b' referenced"},
|
||||
{"a: &a\n b: *a\n", "YAML error: Anchor 'a' value contains itself"},
|
||||
{"value: -", "YAML error: block sequence entries are not allowed in this context"},
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalErrors(c *C) {
|
||||
for _, item := range unmarshalErrorTests {
|
||||
var value interface{}
|
||||
err := goyaml.Unmarshal([]byte(item.data), &value)
|
||||
c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
|
||||
}
|
||||
}
|
||||
|
||||
var setterTests = []struct {
|
||||
data, tag string
|
||||
value interface{}
|
||||
}{
|
||||
{"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}},
|
||||
{"_: [1,A]", "!!seq", []interface{}{1, "A"}},
|
||||
{"_: 10", "!!int", 10},
|
||||
{"_: null", "!!null", nil},
|
||||
{"_: !!foo 'BAR!'", "!!foo", "BAR!"},
|
||||
}
|
||||
|
||||
var setterResult = map[int]bool{}
|
||||
|
||||
type typeWithSetter struct {
|
||||
tag string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (o *typeWithSetter) SetYAML(tag string, value interface{}) (ok bool) {
|
||||
o.tag = tag
|
||||
o.value = value
|
||||
if i, ok := value.(int); ok {
|
||||
if result, ok := setterResult[i]; ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type typeWithSetterField struct {
|
||||
Field *typeWithSetter "_"
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalWithSetter(c *C) {
|
||||
for _, item := range setterTests {
|
||||
obj := &typeWithSetterField{}
|
||||
err := goyaml.Unmarshal([]byte(item.data), obj)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(obj.Field, NotNil,
|
||||
Commentf("Pointer not initialized (%#v)", item.value))
|
||||
c.Assert(obj.Field.tag, Equals, item.tag)
|
||||
c.Assert(obj.Field.value, DeepEquals, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalWholeDocumentWithSetter(c *C) {
|
||||
obj := &typeWithSetter{}
|
||||
err := goyaml.Unmarshal([]byte(setterTests[0].data), obj)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(obj.tag, Equals, setterTests[0].tag)
|
||||
value, ok := obj.value.(map[interface{}]interface{})
|
||||
c.Assert(ok, Equals, true)
|
||||
c.Assert(value["_"], DeepEquals, setterTests[0].value)
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalWithFalseSetterIgnoresValue(c *C) {
|
||||
setterResult[2] = false
|
||||
setterResult[4] = false
|
||||
defer func() {
|
||||
delete(setterResult, 2)
|
||||
delete(setterResult, 4)
|
||||
}()
|
||||
|
||||
m := map[string]*typeWithSetter{}
|
||||
data := "{abc: 1, def: 2, ghi: 3, jkl: 4}"
|
||||
err := goyaml.Unmarshal([]byte(data), m)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(m["abc"], NotNil)
|
||||
c.Assert(m["def"], IsNil)
|
||||
c.Assert(m["ghi"], NotNil)
|
||||
c.Assert(m["jkl"], IsNil)
|
||||
|
||||
c.Assert(m["abc"].value, Equals, 1)
|
||||
c.Assert(m["ghi"].value, Equals, 3)
|
||||
}
|
||||
|
||||
//var data []byte
|
||||
//func init() {
|
||||
// var err error
|
||||
// data, err = ioutil.ReadFile("/tmp/file.yaml")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s *S) BenchmarkUnmarshal(c *C) {
|
||||
// var err error
|
||||
// for i := 0; i < c.N; i++ {
|
||||
// var v map[string]interface{}
|
||||
// err = goyaml.Unmarshal(data, &v)
|
||||
// }
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (s *S) BenchmarkMarshal(c *C) {
|
||||
// var v map[string]interface{}
|
||||
// goyaml.Unmarshal(data, &v)
|
||||
// c.ResetTimer()
|
||||
// for i := 0; i < c.N; i++ {
|
||||
// goyaml.Marshal(&v)
|
||||
// }
|
||||
//}
|
||||
1682
third_party/launchpad.net/goyaml/emitterc.go
vendored
Normal file
1682
third_party/launchpad.net/goyaml/emitterc.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
221
third_party/launchpad.net/goyaml/encode.go
vendored
Normal file
221
third_party/launchpad.net/goyaml/encode.go
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type encoder struct {
|
||||
emitter yaml_emitter_t
|
||||
event yaml_event_t
|
||||
out []byte
|
||||
flow bool
|
||||
}
|
||||
|
||||
func newEncoder() (e *encoder) {
|
||||
e = &encoder{}
|
||||
e.must(yaml_emitter_initialize(&e.emitter))
|
||||
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
||||
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
|
||||
e.emit()
|
||||
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
|
||||
e.emit()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *encoder) finish() {
|
||||
e.must(yaml_document_end_event_initialize(&e.event, true))
|
||||
e.emit()
|
||||
e.emitter.open_ended = false
|
||||
e.must(yaml_stream_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) destroy() {
|
||||
yaml_emitter_delete(&e.emitter)
|
||||
}
|
||||
|
||||
func (e *encoder) emit() {
|
||||
// This will internally delete the e.event value.
|
||||
if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT {
|
||||
e.must(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) must(ok bool) {
|
||||
if !ok {
|
||||
msg := e.emitter.problem
|
||||
if msg == "" {
|
||||
msg = "Unknown problem generating YAML content"
|
||||
}
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) marshal(tag string, in reflect.Value) {
|
||||
var value interface{}
|
||||
if getter, ok := in.Interface().(Getter); ok {
|
||||
tag, value = getter.GetYAML()
|
||||
if value == nil {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
in = reflect.ValueOf(value)
|
||||
}
|
||||
switch in.Kind() {
|
||||
case reflect.Interface:
|
||||
if in.IsNil() {
|
||||
e.nilv()
|
||||
} else {
|
||||
e.marshal(tag, in.Elem())
|
||||
}
|
||||
case reflect.Map:
|
||||
e.mapv(tag, in)
|
||||
case reflect.Ptr:
|
||||
if in.IsNil() {
|
||||
e.nilv()
|
||||
} else {
|
||||
e.marshal(tag, in.Elem())
|
||||
}
|
||||
case reflect.Struct:
|
||||
e.structv(tag, in)
|
||||
case reflect.Slice:
|
||||
e.slicev(tag, in)
|
||||
case reflect.String:
|
||||
e.stringv(tag, in)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
e.intv(tag, in)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
e.uintv(tag, in)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
e.floatv(tag, in)
|
||||
case reflect.Bool:
|
||||
e.boolv(tag, in)
|
||||
default:
|
||||
panic("Can't marshal type yet: " + in.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) mapv(tag string, in reflect.Value) {
|
||||
e.mappingv(tag, func() {
|
||||
keys := keyList(in.MapKeys())
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
e.marshal("", k)
|
||||
e.marshal("", in.MapIndex(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) structv(tag string, in reflect.Value) {
|
||||
sinfo, err := getStructInfo(in.Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e.mappingv(tag, func() {
|
||||
for _, info := range sinfo.FieldsList {
|
||||
var value reflect.Value
|
||||
if info.Inline == nil {
|
||||
value = in.Field(info.Num)
|
||||
} else {
|
||||
value = in.FieldByIndex(info.Inline)
|
||||
}
|
||||
if info.OmitEmpty && isZero(value) {
|
||||
continue
|
||||
}
|
||||
e.marshal("", reflect.ValueOf(info.Key))
|
||||
e.flow = info.Flow
|
||||
e.marshal("", value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) mappingv(tag string, f func()) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_MAPPING_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_MAPPING_STYLE
|
||||
}
|
||||
e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
||||
e.emit()
|
||||
f()
|
||||
e.must(yaml_mapping_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) slicev(tag string, in reflect.Value) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_SEQUENCE_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_SEQUENCE_STYLE
|
||||
}
|
||||
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
||||
e.emit()
|
||||
n := in.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
e.marshal("", in.Index(i))
|
||||
}
|
||||
e.must(yaml_sequence_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) stringv(tag string, in reflect.Value) {
|
||||
var style yaml_scalar_style_t
|
||||
s := in.String()
|
||||
if rtag, _ := resolve("", s); rtag != "!!str" {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
} else {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
e.emitScalar(s, "", tag, style)
|
||||
}
|
||||
|
||||
func (e *encoder) boolv(tag string, in reflect.Value) {
|
||||
var s string
|
||||
if in.Bool() {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) intv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatInt(in.Int(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) uintv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatUint(in.Uint(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) floatv(tag string, in reflect.Value) {
|
||||
// FIXME: Handle 64 bits here.
|
||||
s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
|
||||
switch s {
|
||||
case "+Inf":
|
||||
s = ".inf"
|
||||
case "-Inf":
|
||||
s = "-.inf"
|
||||
case "NaN":
|
||||
s = ".nan"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) nilv() {
|
||||
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
||||
implicit := tag == ""
|
||||
if !implicit {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
||||
e.emit()
|
||||
}
|
||||
379
third_party/launchpad.net/goyaml/encode_test.go
vendored
Normal file
379
third_party/launchpad.net/goyaml/encode_test.go
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
package goyaml_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "launchpad.net/gocheck"
|
||||
"github.com/coreos/coreos-cloudinit/third_party/launchpad.net/goyaml"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var marshalIntTest = 123
|
||||
|
||||
var marshalTests = []struct {
|
||||
value interface{}
|
||||
data string
|
||||
}{
|
||||
{
|
||||
&struct{}{},
|
||||
"{}\n",
|
||||
}, {
|
||||
map[string]string{"v": "hi"},
|
||||
"v: hi\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": "hi"},
|
||||
"v: hi\n",
|
||||
}, {
|
||||
map[string]string{"v": "true"},
|
||||
"v: \"true\"\n",
|
||||
}, {
|
||||
map[string]string{"v": "false"},
|
||||
"v: \"false\"\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": true},
|
||||
"v: true\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": false},
|
||||
"v: false\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": 10},
|
||||
"v: 10\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": -10},
|
||||
"v: -10\n",
|
||||
}, {
|
||||
map[string]uint{"v": 42},
|
||||
"v: 42\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": int64(4294967296)},
|
||||
"v: 4294967296\n",
|
||||
}, {
|
||||
map[string]int64{"v": int64(4294967296)},
|
||||
"v: 4294967296\n",
|
||||
}, {
|
||||
map[string]uint64{"v": 4294967296},
|
||||
"v: 4294967296\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": "10"},
|
||||
"v: \"10\"\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": 0.1},
|
||||
"v: 0.1\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": float64(0.1)},
|
||||
"v: 0.1\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": -0.1},
|
||||
"v: -0.1\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": math.Inf(+1)},
|
||||
"v: .inf\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": math.Inf(-1)},
|
||||
"v: -.inf\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": math.NaN()},
|
||||
"v: .nan\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": nil},
|
||||
"v: null\n",
|
||||
}, {
|
||||
map[string]interface{}{"v": ""},
|
||||
"v: \"\"\n",
|
||||
}, {
|
||||
map[string][]string{"v": []string{"A", "B"}},
|
||||
"v:\n- A\n- B\n",
|
||||
}, {
|
||||
map[string][]string{"v": []string{"A", "B\nC"}},
|
||||
"v:\n- A\n- 'B\n\n C'\n",
|
||||
}, {
|
||||
map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
|
||||
"v:\n- A\n- 1\n- B:\n - 2\n - 3\n",
|
||||
}, {
|
||||
map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
|
||||
"a:\n b: c\n",
|
||||
}, {
|
||||
map[string]interface{}{"a": "-"},
|
||||
"a: '-'\n",
|
||||
},
|
||||
|
||||
// Simple values.
|
||||
{
|
||||
&marshalIntTest,
|
||||
"123\n",
|
||||
},
|
||||
|
||||
// Structures
|
||||
{
|
||||
&struct{ Hello string }{"world"},
|
||||
"hello: world\n",
|
||||
}, {
|
||||
&struct {
|
||||
A struct {
|
||||
B string
|
||||
}
|
||||
}{struct{ B string }{"c"}},
|
||||
"a:\n b: c\n",
|
||||
}, {
|
||||
&struct {
|
||||
A *struct {
|
||||
B string
|
||||
}
|
||||
}{&struct{ B string }{"c"}},
|
||||
"a:\n b: c\n",
|
||||
}, {
|
||||
&struct {
|
||||
A *struct {
|
||||
B string
|
||||
}
|
||||
}{},
|
||||
"a: null\n",
|
||||
}, {
|
||||
&struct{ A int }{1},
|
||||
"a: 1\n",
|
||||
}, {
|
||||
&struct{ A []int }{[]int{1, 2}},
|
||||
"a:\n- 1\n- 2\n",
|
||||
}, {
|
||||
&struct {
|
||||
B int "a"
|
||||
}{1},
|
||||
"a: 1\n",
|
||||
}, {
|
||||
&struct{ A bool }{true},
|
||||
"a: true\n",
|
||||
},
|
||||
|
||||
// Conditional flag
|
||||
{
|
||||
&struct {
|
||||
A int "a,omitempty"
|
||||
B int "b,omitempty"
|
||||
}{1, 0},
|
||||
"a: 1\n",
|
||||
}, {
|
||||
&struct {
|
||||
A int "a,omitempty"
|
||||
B int "b,omitempty"
|
||||
}{0, 0},
|
||||
"{}\n",
|
||||
}, {
|
||||
&struct {
|
||||
A *struct{ X int } "a,omitempty"
|
||||
B int "b,omitempty"
|
||||
}{nil, 0},
|
||||
"{}\n",
|
||||
},
|
||||
|
||||
// Flow flag
|
||||
{
|
||||
&struct {
|
||||
A []int "a,flow"
|
||||
}{[]int{1, 2}},
|
||||
"a: [1, 2]\n",
|
||||
}, {
|
||||
&struct {
|
||||
A map[string]string "a,flow"
|
||||
}{map[string]string{"b": "c", "d": "e"}},
|
||||
"a: {b: c, d: e}\n",
|
||||
}, {
|
||||
&struct {
|
||||
A struct {
|
||||
B, D string
|
||||
} "a,flow"
|
||||
}{struct{ B, D string }{"c", "e"}},
|
||||
"a: {b: c, d: e}\n",
|
||||
},
|
||||
|
||||
// Unexported field
|
||||
{
|
||||
&struct {
|
||||
u int
|
||||
A int
|
||||
}{0, 1},
|
||||
"a: 1\n",
|
||||
},
|
||||
|
||||
// Ignored field
|
||||
{
|
||||
&struct {
|
||||
A int
|
||||
B int "-"
|
||||
}{1, 2},
|
||||
"a: 1\n",
|
||||
},
|
||||
|
||||
// Struct inlining
|
||||
{
|
||||
&struct {
|
||||
A int
|
||||
C inlineB `yaml:",inline"`
|
||||
}{1, inlineB{2, inlineC{3}}},
|
||||
"a: 1\nb: 2\nc: 3\n",
|
||||
},
|
||||
}
|
||||
|
||||
func (s *S) TestMarshal(c *C) {
|
||||
for _, item := range marshalTests {
|
||||
data, err := goyaml.Marshal(item.value)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(data), Equals, item.data)
|
||||
}
|
||||
}
|
||||
|
||||
var marshalErrorTests = []struct {
|
||||
value interface{}
|
||||
error string
|
||||
}{
|
||||
{
|
||||
&struct {
|
||||
B int
|
||||
inlineB ",inline"
|
||||
}{1, inlineB{2, inlineC{3}}},
|
||||
`Duplicated key 'b' in struct struct \{ B int; .*`,
|
||||
},
|
||||
}
|
||||
|
||||
func (s *S) TestMarshalErrors(c *C) {
|
||||
for _, item := range marshalErrorTests {
|
||||
_, err := goyaml.Marshal(item.value)
|
||||
c.Assert(err, ErrorMatches, item.error)
|
||||
}
|
||||
}
|
||||
|
||||
var marshalTaggedIfaceTest interface{} = &struct{ A string }{"B"}
|
||||
|
||||
var getterTests = []struct {
|
||||
data, tag string
|
||||
value interface{}
|
||||
}{
|
||||
{"_:\n hi: there\n", "", map[interface{}]interface{}{"hi": "there"}},
|
||||
{"_:\n- 1\n- A\n", "", []interface{}{1, "A"}},
|
||||
{"_: 10\n", "", 10},
|
||||
{"_: null\n", "", nil},
|
||||
{"_: !foo BAR!\n", "!foo", "BAR!"},
|
||||
{"_: !foo 1\n", "!foo", "1"},
|
||||
{"_: !foo '\"1\"'\n", "!foo", "\"1\""},
|
||||
{"_: !foo 1.1\n", "!foo", 1.1},
|
||||
{"_: !foo 1\n", "!foo", 1},
|
||||
{"_: !foo 1\n", "!foo", uint(1)},
|
||||
{"_: !foo true\n", "!foo", true},
|
||||
{"_: !foo\n- A\n- B\n", "!foo", []string{"A", "B"}},
|
||||
{"_: !foo\n A: B\n", "!foo", map[string]string{"A": "B"}},
|
||||
{"_: !foo\n a: B\n", "!foo", &marshalTaggedIfaceTest},
|
||||
}
|
||||
|
||||
func (s *S) TestMarshalTypeCache(c *C) {
|
||||
var data []byte
|
||||
var err error
|
||||
func() {
|
||||
type T struct{ A int }
|
||||
data, err = goyaml.Marshal(&T{})
|
||||
c.Assert(err, IsNil)
|
||||
}()
|
||||
func() {
|
||||
type T struct{ B int }
|
||||
data, err = goyaml.Marshal(&T{})
|
||||
c.Assert(err, IsNil)
|
||||
}()
|
||||
c.Assert(string(data), Equals, "b: 0\n")
|
||||
}
|
||||
|
||||
type typeWithGetter struct {
|
||||
tag string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (o typeWithGetter) GetYAML() (tag string, value interface{}) {
|
||||
return o.tag, o.value
|
||||
}
|
||||
|
||||
type typeWithGetterField struct {
|
||||
Field typeWithGetter "_"
|
||||
}
|
||||
|
||||
func (s *S) TestMashalWithGetter(c *C) {
|
||||
for _, item := range getterTests {
|
||||
obj := &typeWithGetterField{}
|
||||
obj.Field.tag = item.tag
|
||||
obj.Field.value = item.value
|
||||
data, err := goyaml.Marshal(obj)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(data), Equals, string(item.data))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestUnmarshalWholeDocumentWithGetter(c *C) {
|
||||
obj := &typeWithGetter{}
|
||||
obj.tag = ""
|
||||
obj.value = map[string]string{"hello": "world!"}
|
||||
data, err := goyaml.Marshal(obj)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(string(data), Equals, "hello: world!\n")
|
||||
}
|
||||
|
||||
func (s *S) TestSortedOutput(c *C) {
|
||||
order := []interface{}{
|
||||
false,
|
||||
true,
|
||||
1,
|
||||
uint(1),
|
||||
1.0,
|
||||
1.1,
|
||||
1.2,
|
||||
2,
|
||||
uint(2),
|
||||
2.0,
|
||||
2.1,
|
||||
"",
|
||||
".1",
|
||||
".2",
|
||||
".a",
|
||||
"1",
|
||||
"2",
|
||||
"a!10",
|
||||
"a/2",
|
||||
"a/10",
|
||||
"a~10",
|
||||
"ab/1",
|
||||
"b/1",
|
||||
"b/01",
|
||||
"b/2",
|
||||
"b/02",
|
||||
"b/3",
|
||||
"b/03",
|
||||
"b1",
|
||||
"b01",
|
||||
"b3",
|
||||
"c2.10",
|
||||
"c10.2",
|
||||
"d1",
|
||||
"d12",
|
||||
"d12a",
|
||||
}
|
||||
m := make(map[interface{}]int)
|
||||
for _, k := range order {
|
||||
m[k] = 1
|
||||
}
|
||||
data, err := goyaml.Marshal(m)
|
||||
c.Assert(err, IsNil)
|
||||
out := "\n" + string(data)
|
||||
last := 0
|
||||
for i, k := range order {
|
||||
repr := fmt.Sprint(k)
|
||||
if s, ok := k.(string); ok {
|
||||
if _, err = strconv.ParseFloat(repr, 32); s == "" || err == nil {
|
||||
repr = `"` + repr + `"`
|
||||
}
|
||||
}
|
||||
index := strings.Index(out, "\n"+repr+":")
|
||||
if index == -1 {
|
||||
c.Fatalf("%#v is not in the output: %#v", k, out)
|
||||
}
|
||||
if index < last {
|
||||
c.Fatalf("%#v was generated before %#v: %q", k, order[i-1], out)
|
||||
}
|
||||
last = index
|
||||
}
|
||||
}
|
||||
300
third_party/launchpad.net/goyaml/goyaml.go
vendored
Normal file
300
third_party/launchpad.net/goyaml/goyaml.go
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// Package goyaml implements YAML support for the Go language.
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func handleErr(err *error) {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
panic(r)
|
||||
} else if _, ok := r.(*reflect.ValueError); ok {
|
||||
panic(r)
|
||||
} else if _, ok := r.(externalPanic); ok {
|
||||
panic(r)
|
||||
} else if s, ok := r.(string); ok {
|
||||
*err = errors.New("YAML error: " + s)
|
||||
} else if e, ok := r.(error); ok {
|
||||
*err = e
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Objects implementing the goyaml.Setter interface will receive the YAML
|
||||
// tag and value via the SetYAML method during unmarshaling, rather than
|
||||
// being implicitly assigned by the goyaml machinery. If setting the value
|
||||
// works, the method should return true. If it returns false, the given
|
||||
// value will be omitted from maps and slices.
|
||||
type Setter interface {
|
||||
SetYAML(tag string, value interface{}) bool
|
||||
}
|
||||
|
||||
// Objects implementing the goyaml.Getter interface will get the GetYAML()
|
||||
// method called when goyaml is requested to marshal the given value, and
|
||||
// the result of this method will be marshaled in place of the actual object.
|
||||
type Getter interface {
|
||||
GetYAML() (tag string, value interface{})
|
||||
}
|
||||
|
||||
// Unmarshal decodes the first document found within the in byte slice
|
||||
// and assigns decoded values into the object pointed by out.
|
||||
//
|
||||
// Maps, pointers to structs and ints, etc, may all be used as out values.
|
||||
// If an internal pointer within a struct is not initialized, goyaml
|
||||
// will initialize it if necessary for unmarshalling the provided data,
|
||||
// but the struct provided as out must not be a nil pointer.
|
||||
//
|
||||
// The type of the decoded values and the type of out will be considered,
|
||||
// and Unmarshal() will do the best possible job to unmarshal values
|
||||
// appropriately. It is NOT considered an error, though, to skip values
|
||||
// because they are not available in the decoded YAML, or if they are not
|
||||
// compatible with the out value. To ensure something was properly
|
||||
// unmarshaled use a map or compare against the previous value for the
|
||||
// field (usually the zero value).
|
||||
//
|
||||
// Struct fields are only unmarshalled if they are exported (have an
|
||||
// upper case first letter), and will be unmarshalled using the field
|
||||
// name lowercased by default. When custom field names are desired, the
|
||||
// tag value may be used to tweak the name. Everything before the first
|
||||
// comma in the field tag will be used as the name. The values following
|
||||
// the comma are used to tweak the marshalling process (see Marshal).
|
||||
// Conflicting names result in a runtime error.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int `yaml:"a,omitempty"`
|
||||
// B int
|
||||
// }
|
||||
// var T t
|
||||
// goyaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
||||
//
|
||||
// See the documentation of Marshal for the format of tags and a list of
|
||||
// supported tag options.
|
||||
//
|
||||
func Unmarshal(in []byte, out interface{}) (err error) {
|
||||
defer handleErr(&err)
|
||||
d := newDecoder()
|
||||
p := newParser(in)
|
||||
defer p.destroy()
|
||||
node := p.parse()
|
||||
if node != nil {
|
||||
d.unmarshal(node, reflect.ValueOf(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal serializes the value provided into a YAML document. The structure
|
||||
// of the generated document will reflect the structure of the value itself.
|
||||
// Maps, pointers to structs and ints, etc, may all be used as the in value.
|
||||
//
|
||||
// In the case of struct values, only exported fields will be serialized.
|
||||
// The lowercased field name is used as the key for each exported field,
|
||||
// but this behavior may be changed using the respective field tag.
|
||||
// The tag may also contain flags to tweak the marshalling behavior for
|
||||
// the field. Conflicting names result in a runtime error. The tag format
|
||||
// accepted is:
|
||||
//
|
||||
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
|
||||
//
|
||||
// The following flags are currently supported:
|
||||
//
|
||||
// omitempty Only include the field if it's not set to the zero
|
||||
// value for the type or to empty slices or maps.
|
||||
// Does not apply to zero valued structs.
|
||||
//
|
||||
// flow Marshal using a flow style (useful for structs,
|
||||
// sequences and maps.
|
||||
//
|
||||
// inline Inline the struct it's applied to, so its fields
|
||||
// are processed as if they were part of the outer
|
||||
// struct.
|
||||
//
|
||||
// In addition, if the key is "-", the field is ignored.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int "a,omitempty"
|
||||
// B int
|
||||
// }
|
||||
// goyaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
||||
// goyaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
||||
//
|
||||
func Marshal(in interface{}) (out []byte, err error) {
|
||||
defer handleErr(&err)
|
||||
e := newEncoder()
|
||||
defer e.destroy()
|
||||
e.marshal("", reflect.ValueOf(in))
|
||||
e.finish()
|
||||
out = e.out
|
||||
return
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Maintain a mapping of keys to structure field indexes
|
||||
|
||||
// The code in this section was copied from gobson.
|
||||
|
||||
// structInfo holds details for the serialization of fields of
|
||||
// a given struct.
|
||||
type structInfo struct {
|
||||
FieldsMap map[string]fieldInfo
|
||||
FieldsList []fieldInfo
|
||||
|
||||
// InlineMap is the number of the field in the struct that
|
||||
// contains an ,inline map, or -1 if there's none.
|
||||
InlineMap int
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
Key string
|
||||
Num int
|
||||
OmitEmpty bool
|
||||
Flow bool
|
||||
|
||||
// Inline holds the field index if the field is part of an inlined struct.
|
||||
Inline []int
|
||||
}
|
||||
|
||||
var structMap = make(map[reflect.Type]*structInfo)
|
||||
var fieldMapMutex sync.RWMutex
|
||||
|
||||
type externalPanic string
|
||||
|
||||
func (e externalPanic) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||
fieldMapMutex.RLock()
|
||||
sinfo, found := structMap[st]
|
||||
fieldMapMutex.RUnlock()
|
||||
if found {
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
n := st.NumField()
|
||||
fieldsMap := make(map[string]fieldInfo)
|
||||
fieldsList := make([]fieldInfo, 0, n)
|
||||
inlineMap := -1
|
||||
for i := 0; i != n; i++ {
|
||||
field := st.Field(i)
|
||||
if field.PkgPath != "" {
|
||||
continue // Private field
|
||||
}
|
||||
|
||||
info := fieldInfo{Num: i}
|
||||
|
||||
tag := field.Tag.Get("yaml")
|
||||
if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
|
||||
tag = string(field.Tag)
|
||||
}
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
inline := false
|
||||
fields := strings.Split(tag, ",")
|
||||
if len(fields) > 1 {
|
||||
for _, flag := range fields[1:] {
|
||||
switch flag {
|
||||
case "omitempty":
|
||||
info.OmitEmpty = true
|
||||
case "flow":
|
||||
info.Flow = true
|
||||
case "inline":
|
||||
inline = true
|
||||
default:
|
||||
msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
|
||||
panic(externalPanic(msg))
|
||||
}
|
||||
}
|
||||
tag = fields[0]
|
||||
}
|
||||
|
||||
if inline {
|
||||
switch field.Type.Kind() {
|
||||
//case reflect.Map:
|
||||
// if inlineMap >= 0 {
|
||||
// return nil, errors.New("Multiple ,inline maps in struct " + st.String())
|
||||
// }
|
||||
// if field.Type.Key() != reflect.TypeOf("") {
|
||||
// return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
|
||||
// }
|
||||
// inlineMap = info.Num
|
||||
case reflect.Struct:
|
||||
sinfo, err := getStructInfo(field.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, finfo := range sinfo.FieldsList {
|
||||
if _, found := fieldsMap[finfo.Key]; found {
|
||||
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
if finfo.Inline == nil {
|
||||
finfo.Inline = []int{i, finfo.Num}
|
||||
} else {
|
||||
finfo.Inline = append([]int{i}, finfo.Inline...)
|
||||
}
|
||||
fieldsMap[finfo.Key] = finfo
|
||||
fieldsList = append(fieldsList, finfo)
|
||||
}
|
||||
default:
|
||||
//panic("Option ,inline needs a struct value or map field")
|
||||
panic("Option ,inline needs a struct value field")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag != "" {
|
||||
info.Key = tag
|
||||
} else {
|
||||
info.Key = strings.ToLower(field.Name)
|
||||
}
|
||||
|
||||
if _, found = fieldsMap[info.Key]; found {
|
||||
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
fieldsList = append(fieldsList, info)
|
||||
fieldsMap[info.Key] = info
|
||||
}
|
||||
|
||||
sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
|
||||
|
||||
fieldMapMutex.Lock()
|
||||
structMap[st] = sinfo
|
||||
fieldMapMutex.Unlock()
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return len(v.String()) == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
case reflect.Slice:
|
||||
return v.Len() == 0
|
||||
case reflect.Map:
|
||||
return v.Len() == 0
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
}
|
||||
return false
|
||||
}
|
||||
1096
third_party/launchpad.net/goyaml/parserc.go
vendored
Normal file
1096
third_party/launchpad.net/goyaml/parserc.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
391
third_party/launchpad.net/goyaml/readerc.go
vendored
Normal file
391
third_party/launchpad.net/goyaml/readerc.go
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Set the reader error and return 0.
|
||||
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
|
||||
parser.error = yaml_READER_ERROR
|
||||
parser.problem = problem
|
||||
parser.problem_offset = offset
|
||||
parser.problem_value = value
|
||||
return false
|
||||
}
|
||||
|
||||
// Byte order marks.
|
||||
const (
|
||||
bom_UTF8 = "\xef\xbb\xbf"
|
||||
bom_UTF16LE = "\xff\xfe"
|
||||
bom_UTF16BE = "\xfe\xff"
|
||||
)
|
||||
|
||||
// Determine the input stream encoding by checking the BOM symbol. If no BOM is
|
||||
// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
|
||||
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
|
||||
// Ensure that we had enough bytes in the raw buffer.
|
||||
for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the encoding.
|
||||
buf := parser.raw_buffer
|
||||
pos := parser.raw_buffer_pos
|
||||
avail := len(buf) - pos
|
||||
if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
|
||||
parser.encoding = yaml_UTF16LE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
|
||||
parser.encoding = yaml_UTF16BE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
parser.raw_buffer_pos += 3
|
||||
parser.offset += 3
|
||||
} else {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Update the raw buffer.
|
||||
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
|
||||
size_read := 0
|
||||
|
||||
// Return if the raw buffer is full.
|
||||
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Return on EOF.
|
||||
if parser.eof {
|
||||
return true
|
||||
}
|
||||
|
||||
// Move the remaining bytes in the raw buffer to the beginning.
|
||||
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
|
||||
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
|
||||
}
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
|
||||
parser.raw_buffer_pos = 0
|
||||
|
||||
// Call the read handler to fill the buffer.
|
||||
size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
|
||||
if err == io.EOF {
|
||||
parser.eof = true
|
||||
} else if err != nil {
|
||||
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure that the buffer contains at least `length` characters.
|
||||
// Return true on success, false on failure.
|
||||
//
|
||||
// The length is supposed to be significantly less that the buffer size.
|
||||
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
|
||||
if parser.read_handler == nil {
|
||||
panic("read handler must be set")
|
||||
}
|
||||
|
||||
// If the EOF flag is set and the raw buffer is empty, do nothing.
|
||||
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Return if the buffer contains enough characters.
|
||||
if parser.unread >= length {
|
||||
return true
|
||||
}
|
||||
|
||||
// Determine the input encoding if it is not known yet.
|
||||
if parser.encoding == yaml_ANY_ENCODING {
|
||||
if !yaml_parser_determine_encoding(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Move the unread characters to the beginning of the buffer.
|
||||
buffer_len := len(parser.buffer)
|
||||
if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
|
||||
copy(parser.buffer, parser.buffer[parser.buffer_pos:])
|
||||
buffer_len -= parser.buffer_pos
|
||||
parser.buffer_pos = 0
|
||||
} else if parser.buffer_pos == buffer_len {
|
||||
buffer_len = 0
|
||||
parser.buffer_pos = 0
|
||||
}
|
||||
|
||||
// Open the whole buffer for writing, and cut it before returning.
|
||||
parser.buffer = parser.buffer[:cap(parser.buffer)]
|
||||
|
||||
// Fill the buffer until it has enough characters.
|
||||
first := true
|
||||
for parser.unread < length {
|
||||
|
||||
// Fill the raw buffer if necessary.
|
||||
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return false
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
// Decode the raw buffer.
|
||||
inner:
|
||||
for parser.raw_buffer_pos != len(parser.raw_buffer) {
|
||||
var value rune
|
||||
var width int
|
||||
|
||||
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
|
||||
|
||||
// Decode the next character.
|
||||
switch parser.encoding {
|
||||
case yaml_UTF8_ENCODING:
|
||||
// Decode a UTF-8 character. Check RFC 3629
|
||||
// (http://www.ietf.org/rfc/rfc3629.txt) for more details.
|
||||
//
|
||||
// The following table (taken from the RFC) is used for
|
||||
// decoding.
|
||||
//
|
||||
// Char. number range | UTF-8 octet sequence
|
||||
// (hexadecimal) | (binary)
|
||||
// --------------------+------------------------------------
|
||||
// 0000 0000-0000 007F | 0xxxxxxx
|
||||
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
//
|
||||
// Additionally, the characters in the range 0xD800-0xDFFF
|
||||
// are prohibited as they are reserved for use with UTF-16
|
||||
// surrogate pairs.
|
||||
|
||||
// Determine the length of the UTF-8 sequence.
|
||||
octet := parser.raw_buffer[parser.raw_buffer_pos]
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
width = 1
|
||||
case octet&0xE0 == 0xC0:
|
||||
width = 2
|
||||
case octet&0xF0 == 0xE0:
|
||||
width = 3
|
||||
case octet&0xF8 == 0xF0:
|
||||
width = 4
|
||||
default:
|
||||
// The leading octet is invalid.
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid leading UTF-8 octet",
|
||||
parser.offset, int(octet))
|
||||
}
|
||||
|
||||
// Check if the raw buffer contains an incomplete character.
|
||||
if width > raw_unread {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-8 octet sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Decode the leading octet.
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
value = rune(octet & 0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
value = rune(octet & 0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
value = rune(octet & 0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
value = rune(octet & 0x07)
|
||||
default:
|
||||
value = 0
|
||||
}
|
||||
|
||||
// Check and decode the trailing octets.
|
||||
for k := 1; k < width; k++ {
|
||||
octet = parser.raw_buffer[parser.raw_buffer_pos+k]
|
||||
|
||||
// Check if the octet is valid.
|
||||
if (octet & 0xC0) != 0x80 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid trailing UTF-8 octet",
|
||||
parser.offset+k, int(octet))
|
||||
}
|
||||
|
||||
// Decode the octet.
|
||||
value = (value << 6) + rune(octet&0x3F)
|
||||
}
|
||||
|
||||
// Check the length of the sequence against the value.
|
||||
switch {
|
||||
case width == 1:
|
||||
case width == 2 && value >= 0x80:
|
||||
case width == 3 && value >= 0x800:
|
||||
case width == 4 && value >= 0x10000:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid length of a UTF-8 sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
|
||||
// Check the range of the value.
|
||||
if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid Unicode character",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
|
||||
var low, high int
|
||||
if parser.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
high, low = 1, 0
|
||||
}
|
||||
|
||||
// The UTF-16 encoding is not as simple as one might
|
||||
// naively think. Check RFC 2781
|
||||
// (http://www.ietf.org/rfc/rfc2781.txt).
|
||||
//
|
||||
// Normally, two subsequent bytes describe a Unicode
|
||||
// character. However a special technique (called a
|
||||
// surrogate pair) is used for specifying character
|
||||
// values larger than 0xFFFF.
|
||||
//
|
||||
// A surrogate pair consists of two pseudo-characters:
|
||||
// high surrogate area (0xD800-0xDBFF)
|
||||
// low surrogate area (0xDC00-0xDFFF)
|
||||
//
|
||||
// The following formulas are used for decoding
|
||||
// and encoding characters using surrogate pairs:
|
||||
//
|
||||
// U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
|
||||
// U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
|
||||
// W1 = 110110yyyyyyyyyy
|
||||
// W2 = 110111xxxxxxxxxx
|
||||
//
|
||||
// where U is the character value, W1 is the high surrogate
|
||||
// area, W2 is the low surrogate area.
|
||||
|
||||
// Check for incomplete UTF-16 character.
|
||||
if raw_unread < 2 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 character",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the character.
|
||||
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
|
||||
|
||||
// Check for unexpected low surrogate area.
|
||||
if value&0xFC00 == 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"unexpected low surrogate area",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Check for a high surrogate area.
|
||||
if value&0xFC00 == 0xD800 {
|
||||
width = 4
|
||||
|
||||
// Check for incomplete surrogate pair.
|
||||
if raw_unread < 4 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 surrogate pair",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the next character.
|
||||
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
|
||||
|
||||
// Check for a low surrogate area.
|
||||
if value2&0xFC00 != 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"expected low surrogate area",
|
||||
parser.offset+2, int(value2))
|
||||
}
|
||||
|
||||
// Generate the value of the surrogate pair.
|
||||
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
|
||||
} else {
|
||||
width = 2
|
||||
}
|
||||
|
||||
default:
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
// Check if the character is in the allowed range:
|
||||
// #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
|
||||
// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
|
||||
// | [#x10000-#x10FFFF] (32 bit)
|
||||
switch {
|
||||
case value == 0x09:
|
||||
case value == 0x0A:
|
||||
case value == 0x0D:
|
||||
case value >= 0x20 && value <= 0x7E:
|
||||
case value == 0x85:
|
||||
case value >= 0xA0 && value <= 0xD7FF:
|
||||
case value >= 0xE000 && value <= 0xFFFD:
|
||||
case value >= 0x10000 && value <= 0x10FFFF:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"control characters are not allowed",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Move the raw pointers.
|
||||
parser.raw_buffer_pos += width
|
||||
parser.offset += width
|
||||
|
||||
// Finally put the character into the buffer.
|
||||
if value <= 0x7F {
|
||||
// 0000 0000-0000 007F . 0xxxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(value)
|
||||
} else if value <= 0x7FF {
|
||||
// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
|
||||
} else if value <= 0xFFFF {
|
||||
// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
|
||||
} else {
|
||||
// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
|
||||
}
|
||||
buffer_len += width
|
||||
|
||||
parser.unread++
|
||||
}
|
||||
|
||||
// On EOF, put NUL into the buffer and return.
|
||||
if parser.eof {
|
||||
parser.buffer[buffer_len] = 0
|
||||
buffer_len++
|
||||
parser.unread++
|
||||
break
|
||||
}
|
||||
}
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return true
|
||||
}
|
||||
155
third_party/launchpad.net/goyaml/resolve.go
vendored
Normal file
155
third_party/launchpad.net/goyaml/resolve.go
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO: merge, timestamps, base 60 floats, omap.
|
||||
|
||||
type resolveMapItem struct {
|
||||
value interface{}
|
||||
tag string
|
||||
}
|
||||
|
||||
var resolveTable = make([]byte, 256)
|
||||
var resolveMap = make(map[string]resolveMapItem)
|
||||
|
||||
func init() {
|
||||
t := resolveTable
|
||||
t[int('+')] = 'S' // Sign
|
||||
t[int('-')] = 'S'
|
||||
for _, c := range "0123456789" {
|
||||
t[int(c)] = 'D' // Digit
|
||||
}
|
||||
for _, c := range "yYnNtTfFoO~" {
|
||||
t[int(c)] = 'M' // In map
|
||||
}
|
||||
t[int('.')] = '.' // Float (potentially in map)
|
||||
t[int('<')] = '<' // Merge
|
||||
|
||||
var resolveMapList = []struct {
|
||||
v interface{}
|
||||
tag string
|
||||
l []string
|
||||
}{
|
||||
{true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}},
|
||||
{true, "!!bool", []string{"true", "True", "TRUE"}},
|
||||
{true, "!!bool", []string{"on", "On", "ON"}},
|
||||
{false, "!!bool", []string{"n", "N", "no", "No", "NO"}},
|
||||
{false, "!!bool", []string{"false", "False", "FALSE"}},
|
||||
{false, "!!bool", []string{"off", "Off", "OFF"}},
|
||||
{nil, "!!null", []string{"~", "null", "Null", "NULL"}},
|
||||
{math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}},
|
||||
{math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}},
|
||||
{math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}},
|
||||
{math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}},
|
||||
}
|
||||
|
||||
m := resolveMap
|
||||
for _, item := range resolveMapList {
|
||||
for _, s := range item.l {
|
||||
m[s] = resolveMapItem{item.v, item.tag}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const longTagPrefix = "tag:yaml.org,2002:"
|
||||
|
||||
func shortTag(tag string) string {
|
||||
if strings.HasPrefix(tag, longTagPrefix) {
|
||||
return "!!" + tag[len(longTagPrefix):]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func resolvableTag(tag string) bool {
|
||||
switch tag {
|
||||
case "", "!!str", "!!bool", "!!int", "!!float", "!!null":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func resolve(tag string, in string) (rtag string, out interface{}) {
|
||||
tag = shortTag(tag)
|
||||
if !resolvableTag(tag) {
|
||||
return tag, in
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tag != "" && tag != rtag {
|
||||
panic("Can't decode " + rtag + " '" + in + "' as a " + tag)
|
||||
}
|
||||
}()
|
||||
|
||||
if in == "" {
|
||||
return "!!null", nil
|
||||
}
|
||||
|
||||
c := resolveTable[in[0]]
|
||||
if c == 0 {
|
||||
// It's a string for sure. Nothing to do.
|
||||
return "!!str", in
|
||||
}
|
||||
|
||||
// Handle things we can lookup in a map.
|
||||
if item, ok := resolveMap[in]; ok {
|
||||
return item.tag, item.value
|
||||
}
|
||||
|
||||
switch c {
|
||||
case 'M':
|
||||
// We've already checked the map above.
|
||||
|
||||
case '.':
|
||||
// Not in the map, so maybe a normal float.
|
||||
floatv, err := strconv.ParseFloat(in, 64)
|
||||
if err == nil {
|
||||
return "!!float", floatv
|
||||
}
|
||||
// XXX Handle base 60 floats here (WTF!)
|
||||
|
||||
case 'D', 'S':
|
||||
// Int, float, or timestamp.
|
||||
for i := 0; i != len(in); i++ {
|
||||
if in[i] == '_' {
|
||||
in = strings.Replace(in, "_", "", -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
intv, err := strconv.ParseInt(in, 0, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return "!!int", int(intv)
|
||||
} else {
|
||||
return "!!int", intv
|
||||
}
|
||||
}
|
||||
floatv, err := strconv.ParseFloat(in, 64)
|
||||
if err == nil {
|
||||
return "!!float", floatv
|
||||
}
|
||||
if strings.HasPrefix(in, "0b") {
|
||||
intv, err := strconv.ParseInt(in[2:], 2, 64)
|
||||
if err == nil {
|
||||
return "!!int", int(intv)
|
||||
}
|
||||
} else if strings.HasPrefix(in, "-0b") {
|
||||
intv, err := strconv.ParseInt(in[3:], 2, 64)
|
||||
if err == nil {
|
||||
return "!!int", -int(intv)
|
||||
}
|
||||
}
|
||||
// XXX Handle timestamps here.
|
||||
|
||||
case '<':
|
||||
// XXX Handle merge (<<) here.
|
||||
|
||||
default:
|
||||
panic("resolveTable item not yet handled: " +
|
||||
string([]byte{c}) + " (with " + in + ")")
|
||||
}
|
||||
return "!!str", in
|
||||
}
|
||||
2710
third_party/launchpad.net/goyaml/scannerc.go
vendored
Normal file
2710
third_party/launchpad.net/goyaml/scannerc.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
104
third_party/launchpad.net/goyaml/sorter.go
vendored
Normal file
104
third_party/launchpad.net/goyaml/sorter.go
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type keyList []reflect.Value
|
||||
|
||||
func (l keyList) Len() int { return len(l) }
|
||||
func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l keyList) Less(i, j int) bool {
|
||||
a := l[i]
|
||||
b := l[j]
|
||||
ak := a.Kind()
|
||||
bk := b.Kind()
|
||||
for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
|
||||
a = a.Elem()
|
||||
ak = a.Kind()
|
||||
}
|
||||
for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
|
||||
b = b.Elem()
|
||||
bk = b.Kind()
|
||||
}
|
||||
af, aok := keyFloat(a)
|
||||
bf, bok := keyFloat(b)
|
||||
if aok && bok {
|
||||
if af != bf {
|
||||
return af < bf
|
||||
}
|
||||
if ak != bk {
|
||||
return ak < bk
|
||||
}
|
||||
return numLess(a, b)
|
||||
}
|
||||
if ak != reflect.String || bk != reflect.String {
|
||||
return ak < bk
|
||||
}
|
||||
ar, br := []rune(a.String()), []rune(b.String())
|
||||
for i := 0; i < len(ar) && i < len(br); i++ {
|
||||
if ar[i] == br[i] {
|
||||
continue
|
||||
}
|
||||
al := unicode.IsLetter(ar[i])
|
||||
bl := unicode.IsLetter(br[i])
|
||||
if al && bl {
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
if al || bl {
|
||||
return bl
|
||||
}
|
||||
var ai, bi int
|
||||
var an, bn int64
|
||||
for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
|
||||
an = an*10 + int64(ar[ai]-'0')
|
||||
}
|
||||
for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
|
||||
bn = bn*10 + int64(br[bi]-'0')
|
||||
}
|
||||
if an != bn {
|
||||
return an < bn
|
||||
}
|
||||
if ai != bi {
|
||||
return ai < bi
|
||||
}
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
return len(ar) < len(br)
|
||||
}
|
||||
|
||||
// keyFloat returns a float value for v if it is a number/bool
|
||||
// and whether it is a number/bool or not.
|
||||
func keyFloat(v reflect.Value) (f float64, ok bool) {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return float64(v.Uint()), true
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return 1, true
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// numLess returns whether a < b.
|
||||
// a and b must necessarily have the same kind.
|
||||
func numLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
}
|
||||
panic("not a number")
|
||||
}
|
||||
12
third_party/launchpad.net/goyaml/suite_test.go
vendored
Normal file
12
third_party/launchpad.net/goyaml/suite_test.go
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package goyaml_test
|
||||
|
||||
import (
|
||||
. "launchpad.net/gocheck"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
type S struct{}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
89
third_party/launchpad.net/goyaml/writerc.go
vendored
Normal file
89
third_party/launchpad.net/goyaml/writerc.go
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package goyaml
|
||||
|
||||
// Set the writer error and return false.
|
||||
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
|
||||
emitter.error = yaml_WRITER_ERROR
|
||||
emitter.problem = problem
|
||||
return false
|
||||
}
|
||||
|
||||
// Flush the output buffer.
|
||||
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
|
||||
if emitter.write_handler == nil {
|
||||
panic("write handler not set")
|
||||
}
|
||||
|
||||
// Check if the buffer is empty.
|
||||
if emitter.buffer_pos == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the output encoding is UTF-8, we don't need to recode the buffer.
|
||||
if emitter.encoding == yaml_UTF8_ENCODING {
|
||||
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Recode the buffer into the raw buffer.
|
||||
var low, high int
|
||||
if emitter.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
high, low = 1, 0
|
||||
}
|
||||
|
||||
pos := 0
|
||||
for pos < emitter.buffer_pos {
|
||||
// See the "reader.c" code for more details on UTF-8 encoding. Note
|
||||
// that we assume that the buffer contains a valid UTF-8 sequence.
|
||||
|
||||
// Read the next UTF-8 character.
|
||||
octet := emitter.buffer[pos]
|
||||
|
||||
var w int
|
||||
var value rune
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
w, value = 1, rune(octet&0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
w, value = 2, rune(octet&0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
w, value = 3, rune(octet&0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
w, value = 4, rune(octet&0x07)
|
||||
}
|
||||
for k := 1; k < w; k++ {
|
||||
octet = emitter.buffer[pos+k]
|
||||
value = (value << 6) + (rune(octet) & 0x3F)
|
||||
}
|
||||
pos += w
|
||||
|
||||
// Write the character.
|
||||
if value < 0x10000 {
|
||||
var b [2]byte
|
||||
b[high] = byte(value >> 8)
|
||||
b[low] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
|
||||
} else {
|
||||
// Write the character using a surrogate pair (check "reader.c").
|
||||
var b [4]byte
|
||||
value -= 0x10000
|
||||
b[high] = byte(0xD8 + (value >> 18))
|
||||
b[low] = byte((value >> 10) & 0xFF)
|
||||
b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
|
||||
b[low+2] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
|
||||
}
|
||||
}
|
||||
|
||||
// Write the raw buffer.
|
||||
if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
emitter.raw_buffer = emitter.raw_buffer[:0]
|
||||
return true
|
||||
}
|
||||
712
third_party/launchpad.net/goyaml/yamlh.go
vendored
Normal file
712
third_party/launchpad.net/goyaml/yamlh.go
vendored
Normal file
@@ -0,0 +1,712 @@
|
||||
package goyaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// The version directive data.
|
||||
type yaml_version_directive_t struct {
|
||||
major int8 // The major version number.
|
||||
minor int8 // The minor version number.
|
||||
}
|
||||
|
||||
// The tag directive data.
|
||||
type yaml_tag_directive_t struct {
|
||||
handle []byte // The tag handle.
|
||||
prefix []byte // The tag prefix.
|
||||
}
|
||||
|
||||
type yaml_encoding_t int
|
||||
|
||||
// The stream encoding.
|
||||
const (
|
||||
// Let the parser choose the encoding.
|
||||
yaml_ANY_ENCODING yaml_encoding_t = iota
|
||||
|
||||
yaml_UTF8_ENCODING // The default UTF-8 encoding.
|
||||
yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM.
|
||||
yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM.
|
||||
)
|
||||
|
||||
type yaml_break_t int
|
||||
|
||||
// Line break types.
|
||||
const (
|
||||
// Let the parser choose the break type.
|
||||
yaml_ANY_BREAK yaml_break_t = iota
|
||||
|
||||
yaml_CR_BREAK // Use CR for line breaks (Mac style).
|
||||
yaml_LN_BREAK // Use LN for line breaks (Unix style).
|
||||
yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style).
|
||||
)
|
||||
|
||||
type yaml_error_type_t int
|
||||
|
||||
// Many bad things could happen with the parser and emitter.
|
||||
const (
|
||||
// No error is produced.
|
||||
yaml_NO_ERROR yaml_error_type_t = iota
|
||||
|
||||
yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory.
|
||||
yaml_READER_ERROR // Cannot read or decode the input stream.
|
||||
yaml_SCANNER_ERROR // Cannot scan the input stream.
|
||||
yaml_PARSER_ERROR // Cannot parse the input stream.
|
||||
yaml_COMPOSER_ERROR // Cannot compose a YAML document.
|
||||
yaml_WRITER_ERROR // Cannot write to the output stream.
|
||||
yaml_EMITTER_ERROR // Cannot emit a YAML stream.
|
||||
)
|
||||
|
||||
// The pointer position.
|
||||
type yaml_mark_t struct {
|
||||
index int // The position index.
|
||||
line int // The position line.
|
||||
column int // The position column.
|
||||
}
|
||||
|
||||
// Node Styles
|
||||
|
||||
type yaml_style_t int8
|
||||
|
||||
type yaml_scalar_style_t yaml_style_t
|
||||
|
||||
// Scalar styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
|
||||
|
||||
yaml_PLAIN_SCALAR_STYLE // The plain scalar style.
|
||||
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
|
||||
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
|
||||
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
|
||||
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
|
||||
)
|
||||
|
||||
type yaml_sequence_style_t yaml_style_t
|
||||
|
||||
// Sequence styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
|
||||
|
||||
yaml_BLOCK_SEQUENCE_STYLE // The block sequence style.
|
||||
yaml_FLOW_SEQUENCE_STYLE // The flow sequence style.
|
||||
)
|
||||
|
||||
type yaml_mapping_style_t yaml_style_t
|
||||
|
||||
// Mapping styles.
|
||||
const (
|
||||
// Let the emitter choose the style.
|
||||
yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
|
||||
|
||||
yaml_BLOCK_MAPPING_STYLE // The block mapping style.
|
||||
yaml_FLOW_MAPPING_STYLE // The flow mapping style.
|
||||
)
|
||||
|
||||
// Tokens
|
||||
|
||||
type yaml_token_type_t int
|
||||
|
||||
// Token types.
|
||||
const (
|
||||
// An empty token.
|
||||
yaml_NO_TOKEN yaml_token_type_t = iota
|
||||
|
||||
yaml_STREAM_START_TOKEN // A STREAM-START token.
|
||||
yaml_STREAM_END_TOKEN // A STREAM-END token.
|
||||
|
||||
yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token.
|
||||
yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token.
|
||||
yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token.
|
||||
yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token.
|
||||
|
||||
yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token.
|
||||
yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token.
|
||||
yaml_BLOCK_END_TOKEN // A BLOCK-END token.
|
||||
|
||||
yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token.
|
||||
yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token.
|
||||
yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token.
|
||||
yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token.
|
||||
|
||||
yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token.
|
||||
yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token.
|
||||
yaml_KEY_TOKEN // A KEY token.
|
||||
yaml_VALUE_TOKEN // A VALUE token.
|
||||
|
||||
yaml_ALIAS_TOKEN // An ALIAS token.
|
||||
yaml_ANCHOR_TOKEN // An ANCHOR token.
|
||||
yaml_TAG_TOKEN // A TAG token.
|
||||
yaml_SCALAR_TOKEN // A SCALAR token.
|
||||
)
|
||||
|
||||
func (tt yaml_token_type_t) String() string {
|
||||
switch tt {
|
||||
case yaml_NO_TOKEN:
|
||||
return "yaml_NO_TOKEN"
|
||||
case yaml_STREAM_START_TOKEN:
|
||||
return "yaml_STREAM_START_TOKEN"
|
||||
case yaml_STREAM_END_TOKEN:
|
||||
return "yaml_STREAM_END_TOKEN"
|
||||
case yaml_VERSION_DIRECTIVE_TOKEN:
|
||||
return "yaml_VERSION_DIRECTIVE_TOKEN"
|
||||
case yaml_TAG_DIRECTIVE_TOKEN:
|
||||
return "yaml_TAG_DIRECTIVE_TOKEN"
|
||||
case yaml_DOCUMENT_START_TOKEN:
|
||||
return "yaml_DOCUMENT_START_TOKEN"
|
||||
case yaml_DOCUMENT_END_TOKEN:
|
||||
return "yaml_DOCUMENT_END_TOKEN"
|
||||
case yaml_BLOCK_SEQUENCE_START_TOKEN:
|
||||
return "yaml_BLOCK_SEQUENCE_START_TOKEN"
|
||||
case yaml_BLOCK_MAPPING_START_TOKEN:
|
||||
return "yaml_BLOCK_MAPPING_START_TOKEN"
|
||||
case yaml_BLOCK_END_TOKEN:
|
||||
return "yaml_BLOCK_END_TOKEN"
|
||||
case yaml_FLOW_SEQUENCE_START_TOKEN:
|
||||
return "yaml_FLOW_SEQUENCE_START_TOKEN"
|
||||
case yaml_FLOW_SEQUENCE_END_TOKEN:
|
||||
return "yaml_FLOW_SEQUENCE_END_TOKEN"
|
||||
case yaml_FLOW_MAPPING_START_TOKEN:
|
||||
return "yaml_FLOW_MAPPING_START_TOKEN"
|
||||
case yaml_FLOW_MAPPING_END_TOKEN:
|
||||
return "yaml_FLOW_MAPPING_END_TOKEN"
|
||||
case yaml_BLOCK_ENTRY_TOKEN:
|
||||
return "yaml_BLOCK_ENTRY_TOKEN"
|
||||
case yaml_FLOW_ENTRY_TOKEN:
|
||||
return "yaml_FLOW_ENTRY_TOKEN"
|
||||
case yaml_KEY_TOKEN:
|
||||
return "yaml_KEY_TOKEN"
|
||||
case yaml_VALUE_TOKEN:
|
||||
return "yaml_VALUE_TOKEN"
|
||||
case yaml_ALIAS_TOKEN:
|
||||
return "yaml_ALIAS_TOKEN"
|
||||
case yaml_ANCHOR_TOKEN:
|
||||
return "yaml_ANCHOR_TOKEN"
|
||||
case yaml_TAG_TOKEN:
|
||||
return "yaml_TAG_TOKEN"
|
||||
case yaml_SCALAR_TOKEN:
|
||||
return "yaml_SCALAR_TOKEN"
|
||||
}
|
||||
return "<unknown token>"
|
||||
}
|
||||
|
||||
// The token structure.
|
||||
type yaml_token_t struct {
|
||||
// The token type.
|
||||
typ yaml_token_type_t
|
||||
|
||||
// The start/end of the token.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
|
||||
// The stream encoding (for yaml_STREAM_START_TOKEN).
|
||||
encoding yaml_encoding_t
|
||||
|
||||
// The alias/anchor/scalar value or tag/tag directive handle
|
||||
// (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN).
|
||||
value []byte
|
||||
|
||||
// The tag suffix (for yaml_TAG_TOKEN).
|
||||
suffix []byte
|
||||
|
||||
// The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN).
|
||||
prefix []byte
|
||||
|
||||
// The scalar style (for yaml_SCALAR_TOKEN).
|
||||
style yaml_scalar_style_t
|
||||
|
||||
// The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN).
|
||||
major, minor int8
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
type yaml_event_type_t int8
|
||||
|
||||
// Event types.
|
||||
const (
|
||||
// An empty event.
|
||||
yaml_NO_EVENT yaml_event_type_t = iota
|
||||
|
||||
yaml_STREAM_START_EVENT // A STREAM-START event.
|
||||
yaml_STREAM_END_EVENT // A STREAM-END event.
|
||||
yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event.
|
||||
yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event.
|
||||
yaml_ALIAS_EVENT // An ALIAS event.
|
||||
yaml_SCALAR_EVENT // A SCALAR event.
|
||||
yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event.
|
||||
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
|
||||
yaml_MAPPING_START_EVENT // A MAPPING-START event.
|
||||
yaml_MAPPING_END_EVENT // A MAPPING-END event.
|
||||
)
|
||||
|
||||
// The event structure.
|
||||
type yaml_event_t struct {
|
||||
|
||||
// The event type.
|
||||
typ yaml_event_type_t
|
||||
|
||||
// The start and end of the event.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
|
||||
// The document encoding (for yaml_STREAM_START_EVENT).
|
||||
encoding yaml_encoding_t
|
||||
|
||||
// The version directive (for yaml_DOCUMENT_START_EVENT).
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
|
||||
tag_directives []yaml_tag_directive_t
|
||||
|
||||
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
|
||||
anchor []byte
|
||||
|
||||
// The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
|
||||
tag []byte
|
||||
|
||||
// The scalar value (for yaml_SCALAR_EVENT).
|
||||
value []byte
|
||||
|
||||
// Is the document start/end indicator implicit, or the tag optional?
|
||||
// (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT).
|
||||
implicit bool
|
||||
|
||||
// Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT).
|
||||
quoted_implicit bool
|
||||
|
||||
// The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
|
||||
style yaml_style_t
|
||||
}
|
||||
|
||||
func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) }
|
||||
func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) }
|
||||
func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) }
|
||||
|
||||
// Nodes
|
||||
|
||||
const (
|
||||
yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null.
|
||||
yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false.
|
||||
yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values.
|
||||
yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values.
|
||||
yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values.
|
||||
yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values.
|
||||
|
||||
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
|
||||
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
|
||||
|
||||
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
|
||||
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
|
||||
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
|
||||
)
|
||||
|
||||
type yaml_node_type_t int
|
||||
|
||||
// Node types.
|
||||
const (
|
||||
// An empty node.
|
||||
yaml_NO_NODE yaml_node_type_t = iota
|
||||
|
||||
yaml_SCALAR_NODE // A scalar node.
|
||||
yaml_SEQUENCE_NODE // A sequence node.
|
||||
yaml_MAPPING_NODE // A mapping node.
|
||||
)
|
||||
|
||||
// An element of a sequence node.
|
||||
type yaml_node_item_t int
|
||||
|
||||
// An element of a mapping node.
|
||||
type yaml_node_pair_t struct {
|
||||
key int // The key of the element.
|
||||
value int // The value of the element.
|
||||
}
|
||||
|
||||
// The node structure.
|
||||
type yaml_node_t struct {
|
||||
typ yaml_node_type_t // The node type.
|
||||
tag []byte // The node tag.
|
||||
|
||||
// The node data.
|
||||
|
||||
// The scalar parameters (for yaml_SCALAR_NODE).
|
||||
scalar struct {
|
||||
value []byte // The scalar value.
|
||||
length int // The length of the scalar value.
|
||||
style yaml_scalar_style_t // The scalar style.
|
||||
}
|
||||
|
||||
// The sequence parameters (for YAML_SEQUENCE_NODE).
|
||||
sequence struct {
|
||||
items_data []yaml_node_item_t // The stack of sequence items.
|
||||
style yaml_sequence_style_t // The sequence style.
|
||||
}
|
||||
|
||||
// The mapping parameters (for yaml_MAPPING_NODE).
|
||||
mapping struct {
|
||||
pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value).
|
||||
pairs_start *yaml_node_pair_t // The beginning of the stack.
|
||||
pairs_end *yaml_node_pair_t // The end of the stack.
|
||||
pairs_top *yaml_node_pair_t // The top of the stack.
|
||||
style yaml_mapping_style_t // The mapping style.
|
||||
}
|
||||
|
||||
start_mark yaml_mark_t // The beginning of the node.
|
||||
end_mark yaml_mark_t // The end of the node.
|
||||
|
||||
}
|
||||
|
||||
// The document structure.
|
||||
type yaml_document_t struct {
|
||||
|
||||
// The document nodes.
|
||||
nodes []yaml_node_t
|
||||
|
||||
// The version directive.
|
||||
version_directive *yaml_version_directive_t
|
||||
|
||||
// The list of tag directives.
|
||||
tag_directives_data []yaml_tag_directive_t
|
||||
tag_directives_start int // The beginning of the tag directives list.
|
||||
tag_directives_end int // The end of the tag directives list.
|
||||
|
||||
start_implicit int // Is the document start indicator implicit?
|
||||
end_implicit int // Is the document end indicator implicit?
|
||||
|
||||
// The start/end of the document.
|
||||
start_mark, end_mark yaml_mark_t
|
||||
}
|
||||
|
||||
// The prototype of a read handler.
|
||||
//
|
||||
// The read handler is called when the parser needs to read more bytes from the
|
||||
// source. The handler should write not more than size bytes to the buffer.
|
||||
// The number of written bytes should be set to the size_read variable.
|
||||
//
|
||||
// [in,out] data A pointer to an application data specified by
|
||||
// yaml_parser_set_input().
|
||||
// [out] buffer The buffer to write the data from the source.
|
||||
// [in] size The size of the buffer.
|
||||
// [out] size_read The actual number of bytes read from the source.
|
||||
//
|
||||
// On success, the handler should return 1. If the handler failed,
|
||||
// the returned value should be 0. On EOF, the handler should set the
|
||||
// size_read to 0 and return 1.
|
||||
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
|
||||
|
||||
// This structure holds information about a potential simple key.
|
||||
type yaml_simple_key_t struct {
|
||||
possible bool // Is a simple key possible?
|
||||
required bool // Is a simple key required?
|
||||
token_number int // The number of the token.
|
||||
mark yaml_mark_t // The position mark.
|
||||
}
|
||||
|
||||
// The states of the parser.
|
||||
type yaml_parser_state_t int
|
||||
|
||||
const (
|
||||
yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
|
||||
|
||||
yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document.
|
||||
yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START.
|
||||
yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document.
|
||||
yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END.
|
||||
yaml_PARSE_BLOCK_NODE_STATE // Expect a block node.
|
||||
yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence.
|
||||
yaml_PARSE_FLOW_NODE_STATE // Expect a flow node.
|
||||
yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence.
|
||||
yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence.
|
||||
yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence.
|
||||
yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
|
||||
yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key.
|
||||
yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value.
|
||||
yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping.
|
||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry.
|
||||
yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
|
||||
yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping.
|
||||
yaml_PARSE_END_STATE // Expect nothing.
|
||||
)
|
||||
|
||||
func (ps yaml_parser_state_t) String() string {
|
||||
switch ps {
|
||||
case yaml_PARSE_STREAM_START_STATE:
|
||||
return "yaml_PARSE_STREAM_START_STATE"
|
||||
case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
|
||||
return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE"
|
||||
case yaml_PARSE_DOCUMENT_START_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_START_STATE"
|
||||
case yaml_PARSE_DOCUMENT_CONTENT_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_CONTENT_STATE"
|
||||
case yaml_PARSE_DOCUMENT_END_STATE:
|
||||
return "yaml_PARSE_DOCUMENT_END_STATE"
|
||||
case yaml_PARSE_BLOCK_NODE_STATE:
|
||||
return "yaml_PARSE_BLOCK_NODE_STATE"
|
||||
case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
|
||||
return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE"
|
||||
case yaml_PARSE_FLOW_NODE_STATE:
|
||||
return "yaml_PARSE_FLOW_NODE_STATE"
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE"
|
||||
case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
|
||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_KEY_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE"
|
||||
case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
|
||||
return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE"
|
||||
case yaml_PARSE_END_STATE:
|
||||
return "yaml_PARSE_END_STATE"
|
||||
}
|
||||
return "<unknown parser state>"
|
||||
}
|
||||
|
||||
// This structure holds aliases data.
|
||||
type yaml_alias_data_t struct {
|
||||
anchor []byte // The anchor.
|
||||
index int // The node id.
|
||||
mark yaml_mark_t // The anchor mark.
|
||||
}
|
||||
|
||||
// The parser structure.
|
||||
//
|
||||
// All members are internal. Manage the structure using the
|
||||
// yaml_parser_ family of functions.
|
||||
type yaml_parser_t struct {
|
||||
|
||||
// Error handling
|
||||
|
||||
error yaml_error_type_t // Error type.
|
||||
|
||||
problem string // Error description.
|
||||
|
||||
// The byte about which the problem occured.
|
||||
problem_offset int
|
||||
problem_value int
|
||||
problem_mark yaml_mark_t
|
||||
|
||||
// The error context.
|
||||
context string
|
||||
context_mark yaml_mark_t
|
||||
|
||||
// Reader stuff
|
||||
|
||||
read_handler yaml_read_handler_t // Read handler.
|
||||
|
||||
input_file io.Reader // File input data.
|
||||
input []byte // String input data.
|
||||
input_pos int
|
||||
|
||||
eof bool // EOF flag
|
||||
|
||||
buffer []byte // The working buffer.
|
||||
buffer_pos int // The current position of the buffer.
|
||||
|
||||
unread int // The number of unread characters in the buffer.
|
||||
|
||||
raw_buffer []byte // The raw buffer.
|
||||
raw_buffer_pos int // The current position of the buffer.
|
||||
|
||||
encoding yaml_encoding_t // The input encoding.
|
||||
|
||||
offset int // The offset of the current position (in bytes).
|
||||
mark yaml_mark_t // The mark of the current position.
|
||||
|
||||
// Scanner stuff
|
||||
|
||||
stream_start_produced bool // Have we started to scan the input stream?
|
||||
stream_end_produced bool // Have we reached the end of the input stream?
|
||||
|
||||
flow_level int // The number of unclosed '[' and '{' indicators.
|
||||
|
||||
tokens []yaml_token_t // The tokens queue.
|
||||
tokens_head int // The head of the tokens queue.
|
||||
tokens_parsed int // The number of tokens fetched from the queue.
|
||||
token_available bool // Does the tokens queue contain a token ready for dequeueing.
|
||||
|
||||
indent int // The current indentation level.
|
||||
indents []int // The indentation levels stack.
|
||||
|
||||
simple_key_allowed bool // May a simple key occur at the current position?
|
||||
simple_keys []yaml_simple_key_t // The stack of simple keys.
|
||||
|
||||
// Parser stuff
|
||||
|
||||
state yaml_parser_state_t // The current parser state.
|
||||
states []yaml_parser_state_t // The parser states stack.
|
||||
marks []yaml_mark_t // The stack of marks.
|
||||
tag_directives []yaml_tag_directive_t // The list of TAG directives.
|
||||
|
||||
// Dumper stuff
|
||||
|
||||
aliases []yaml_alias_data_t // The alias data.
|
||||
|
||||
document *yaml_document_t // The currently parsed document.
|
||||
}
|
||||
|
||||
// Emitter Definitions
|
||||
|
||||
// The prototype of a write handler.
|
||||
//
|
||||
// The write handler is called when the emitter needs to flush the accumulated
|
||||
// characters to the output. The handler should write @a size bytes of the
|
||||
// @a buffer to the output.
|
||||
//
|
||||
// @param[in,out] data A pointer to an application data specified by
|
||||
// yaml_emitter_set_output().
|
||||
// @param[in] buffer The buffer with bytes to be written.
|
||||
// @param[in] size The size of the buffer.
|
||||
//
|
||||
// @returns On success, the handler should return @c 1. If the handler failed,
|
||||
// the returned value should be @c 0.
|
||||
//
|
||||
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
|
||||
|
||||
type yaml_emitter_state_t int
|
||||
|
||||
// The emitter states.
|
||||
const (
|
||||
// Expect STREAM-START.
|
||||
yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
|
||||
|
||||
yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END.
|
||||
yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END.
|
||||
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
|
||||
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
|
||||
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
|
||||
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
|
||||
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
|
||||
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
|
||||
yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence.
|
||||
yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence.
|
||||
yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping.
|
||||
yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping.
|
||||
yaml_EMIT_END_STATE // Expect nothing.
|
||||
)
|
||||
|
||||
// The emitter structure.
|
||||
//
|
||||
// All members are internal. Manage the structure using the @c yaml_emitter_
|
||||
// family of functions.
|
||||
type yaml_emitter_t struct {
|
||||
|
||||
// Error handling
|
||||
|
||||
error yaml_error_type_t // Error type.
|
||||
problem string // Error description.
|
||||
|
||||
// Writer stuff
|
||||
|
||||
write_handler yaml_write_handler_t // Write handler.
|
||||
|
||||
output_buffer *[]byte // String output data.
|
||||
output_file io.Writer // File output data.
|
||||
|
||||
buffer []byte // The working buffer.
|
||||
buffer_pos int // The current position of the buffer.
|
||||
|
||||
raw_buffer []byte // The raw buffer.
|
||||
raw_buffer_pos int // The current position of the buffer.
|
||||
|
||||
encoding yaml_encoding_t // The stream encoding.
|
||||
|
||||
// Emitter stuff
|
||||
|
||||
canonical bool // If the output is in the canonical style?
|
||||
best_indent int // The number of indentation spaces.
|
||||
best_width int // The preferred width of the output lines.
|
||||
unicode bool // Allow unescaped non-ASCII characters?
|
||||
line_break yaml_break_t // The preferred line break.
|
||||
|
||||
state yaml_emitter_state_t // The current emitter state.
|
||||
states []yaml_emitter_state_t // The stack of states.
|
||||
|
||||
events []yaml_event_t // The event queue.
|
||||
events_head int // The head of the event queue.
|
||||
|
||||
indents []int // The stack of indentation levels.
|
||||
|
||||
tag_directives []yaml_tag_directive_t // The list of tag directives.
|
||||
|
||||
indent int // The current indentation level.
|
||||
|
||||
flow_level int // The current flow level.
|
||||
|
||||
root_context bool // Is it the document root context?
|
||||
sequence_context bool // Is it a sequence context?
|
||||
mapping_context bool // Is it a mapping context?
|
||||
simple_key_context bool // Is it a simple mapping key context?
|
||||
|
||||
line int // The current line.
|
||||
column int // The current column.
|
||||
whitespace bool // If the last character was a whitespace?
|
||||
indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
|
||||
open_ended bool // If an explicit document end is required?
|
||||
|
||||
// Anchor analysis.
|
||||
anchor_data struct {
|
||||
anchor []byte // The anchor value.
|
||||
alias bool // Is it an alias?
|
||||
}
|
||||
|
||||
// Tag analysis.
|
||||
tag_data struct {
|
||||
handle []byte // The tag handle.
|
||||
suffix []byte // The tag suffix.
|
||||
}
|
||||
|
||||
// Scalar analysis.
|
||||
scalar_data struct {
|
||||
value []byte // The scalar value.
|
||||
multiline bool // Does the scalar contain line breaks?
|
||||
flow_plain_allowed bool // Can the scalar be expessed in the flow plain style?
|
||||
block_plain_allowed bool // Can the scalar be expressed in the block plain style?
|
||||
single_quoted_allowed bool // Can the scalar be expressed in the single quoted style?
|
||||
block_allowed bool // Can the scalar be expressed in the literal or folded styles?
|
||||
style yaml_scalar_style_t // The output style.
|
||||
}
|
||||
|
||||
// Dumper stuff
|
||||
|
||||
opened bool // If the stream was already opened?
|
||||
closed bool // If the stream was already closed?
|
||||
|
||||
// The information associated with the document nodes.
|
||||
anchors *struct {
|
||||
references int // The number of references.
|
||||
anchor int // The anchor id.
|
||||
serialized bool // If the node has been emitted?
|
||||
}
|
||||
|
||||
last_anchor_id int // The last assigned anchor id.
|
||||
|
||||
document *yaml_document_t // The currently emitted document.
|
||||
}
|
||||
173
third_party/launchpad.net/goyaml/yamlprivateh.go
vendored
Normal file
173
third_party/launchpad.net/goyaml/yamlprivateh.go
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package goyaml
|
||||
|
||||
const (
|
||||
// The size of the input raw buffer.
|
||||
input_raw_buffer_size = 512
|
||||
|
||||
// The size of the input buffer.
|
||||
// It should be possible to decode the whole raw buffer.
|
||||
input_buffer_size = input_raw_buffer_size * 3
|
||||
|
||||
// The size of the output buffer.
|
||||
output_buffer_size = 128
|
||||
|
||||
// The size of the output raw buffer.
|
||||
// It should be possible to encode the whole output buffer.
|
||||
output_raw_buffer_size = (output_buffer_size*2 + 2)
|
||||
|
||||
// The size of other stacks and queues.
|
||||
initial_stack_size = 16
|
||||
initial_queue_size = 16
|
||||
initial_string_size = 16
|
||||
)
|
||||
|
||||
// Check if the character at the specified position is an alphabetical
|
||||
// character, a digit, '_', or '-'.
|
||||
func is_alpha(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a digit.
|
||||
func is_digit(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9'
|
||||
}
|
||||
|
||||
// Get the value of a digit.
|
||||
func as_digit(b []byte, i int) int {
|
||||
return int(b[i]) - '0'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a hex-digit.
|
||||
func is_hex(b []byte, i int) bool {
|
||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f'
|
||||
}
|
||||
|
||||
// Get the value of a hex-digit.
|
||||
func as_hex(b []byte, i int) int {
|
||||
bi := b[i]
|
||||
if bi >= 'A' && bi <= 'F' {
|
||||
return int(bi) - 'A' + 10
|
||||
}
|
||||
if bi >= 'a' && bi <= 'f' {
|
||||
return int(bi) - 'a' + 10
|
||||
}
|
||||
return int(bi) - '0'
|
||||
}
|
||||
|
||||
// Check if the character is ASCII.
|
||||
func is_ascii(b []byte, i int) bool {
|
||||
return b[i] <= 0x7F
|
||||
}
|
||||
|
||||
// Check if the character at the start of the buffer can be printed unescaped.
|
||||
func is_printable(b []byte, i int) bool {
|
||||
return ((b[i] == 0x0A) || // . == #x0A
|
||||
(b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E
|
||||
(b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF
|
||||
(b[i] > 0xC2 && b[i] < 0xED) ||
|
||||
(b[i] == 0xED && b[i+1] < 0xA0) ||
|
||||
(b[i] == 0xEE) ||
|
||||
(b[i] == 0xEF && // #xE000 <= . <= #xFFFD
|
||||
!(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF
|
||||
!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is NUL.
|
||||
func is_z(b []byte, i int) bool {
|
||||
return b[i] == 0x00
|
||||
}
|
||||
|
||||
// Check if the beginning of the buffer is a BOM.
|
||||
func is_bom(b []byte, i int) bool {
|
||||
return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is space.
|
||||
func is_space(b []byte, i int) bool {
|
||||
return b[i] == ' '
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is tab.
|
||||
func is_tab(b []byte, i int) bool {
|
||||
return b[i] == '\t'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is blank (space or tab).
|
||||
func is_blank(b []byte, i int) bool {
|
||||
//return is_space(b, i) || is_tab(b, i)
|
||||
return b[i] == ' ' || b[i] == '\t'
|
||||
}
|
||||
|
||||
// Check if the character at the specified position is a line break.
|
||||
func is_break(b []byte, i int) bool {
|
||||
return (b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029)
|
||||
}
|
||||
|
||||
func is_crlf(b []byte, i int) bool {
|
||||
return b[i] == '\r' && b[i+1] == '\n'
|
||||
}
|
||||
|
||||
// Check if the character is a line break or NUL.
|
||||
func is_breakz(b []byte, i int) bool {
|
||||
//return is_break(b, i) || is_z(b, i)
|
||||
return ( // is_break:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
// is_z:
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Check if the character is a line break, space, or NUL.
|
||||
func is_spacez(b []byte, i int) bool {
|
||||
//return is_space(b, i) || is_breakz(b, i)
|
||||
return ( // is_space:
|
||||
b[i] == ' ' ||
|
||||
// is_breakz:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Check if the character is a line break, space, tab, or NUL.
|
||||
func is_blankz(b []byte, i int) bool {
|
||||
//return is_blank(b, i) || is_breakz(b, i)
|
||||
return ( // is_blank:
|
||||
b[i] == ' ' || b[i] == '\t' ||
|
||||
// is_breakz:
|
||||
b[i] == '\r' || // CR (#xD)
|
||||
b[i] == '\n' || // LF (#xA)
|
||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
|
||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
|
||||
b[i] == 0)
|
||||
}
|
||||
|
||||
// Determine the width of the character.
|
||||
func width(b byte) int {
|
||||
// Don't replace these by a switch without first
|
||||
// confirming that it is being inlined.
|
||||
if b&0x80 == 0x00 {
|
||||
return 1
|
||||
}
|
||||
if b&0xE0 == 0xC0 {
|
||||
return 2
|
||||
}
|
||||
if b&0xF0 == 0xE0 {
|
||||
return 3
|
||||
}
|
||||
if b&0xF8 == 0xF0 {
|
||||
return 4
|
||||
}
|
||||
return 0
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user