Browse Source

Update go-git

Brendan Abolivier 2 years ago
parent
commit
b99ae88b1d
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
100 changed files with 13935 additions and 2 deletions
  1. 34
    2
      vendor/manifest
  2. 2
    0
      vendor/src/github.com/kevinburke/ssh_config/AUTHORS.txt
  3. 49
    0
      vendor/src/github.com/kevinburke/ssh_config/LICENSE
  4. 29
    0
      vendor/src/github.com/kevinburke/ssh_config/Makefile
  5. 81
    0
      vendor/src/github.com/kevinburke/ssh_config/README.md
  6. 639
    0
      vendor/src/github.com/kevinburke/ssh_config/config.go
  7. 340
    0
      vendor/src/github.com/kevinburke/ssh_config/config_test.go
  8. 48
    0
      vendor/src/github.com/kevinburke/ssh_config/example_test.go
  9. 235
    0
      vendor/src/github.com/kevinburke/ssh_config/lexer.go
  10. 182
    0
      vendor/src/github.com/kevinburke/ssh_config/parser.go
  11. 25
    0
      vendor/src/github.com/kevinburke/ssh_config/position.go
  12. 3
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/anotherfile
  13. 39
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/config1
  14. 50
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/config2
  15. 31
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/config3
  16. 4
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/config4
  17. 4
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/eqsign
  18. 2
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/extraspace
  19. 4
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/include
  20. 4
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/include-recursive
  21. 2
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/invalid-port
  22. 2
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/match-directive
  23. 5
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/negated
  24. 0
    0
      vendor/src/github.com/kevinburke/ssh_config/testdata/system-include
  25. 49
    0
      vendor/src/github.com/kevinburke/ssh_config/token.go
  26. 162
    0
      vendor/src/github.com/kevinburke/ssh_config/validators.go
  27. 44
    0
      vendor/src/github.com/kevinburke/ssh_config/validators_test.go
  28. 61
    0
      vendor/src/github.com/pelletier/go-buffruneio/README.md
  29. 133
    0
      vendor/src/github.com/pelletier/go-buffruneio/buffruneio.go
  30. 264
    0
      vendor/src/github.com/pelletier/go-buffruneio/buffruneio_test.go
  31. 526
    0
      vendor/src/golang.org/x/crypto/cast5/cast5.go
  32. 106
    0
      vendor/src/golang.org/x/crypto/cast5/cast5_test.go
  33. 219
    0
      vendor/src/golang.org/x/crypto/openpgp/armor/armor.go
  34. 95
    0
      vendor/src/golang.org/x/crypto/openpgp/armor/armor_test.go
  35. 160
    0
      vendor/src/golang.org/x/crypto/openpgp/armor/encode.go
  36. 59
    0
      vendor/src/golang.org/x/crypto/openpgp/canonical_text.go
  37. 52
    0
      vendor/src/golang.org/x/crypto/openpgp/canonical_text_test.go
  38. 376
    0
      vendor/src/golang.org/x/crypto/openpgp/clearsign/clearsign.go
  39. 210
    0
      vendor/src/golang.org/x/crypto/openpgp/clearsign/clearsign_test.go
  40. 122
    0
      vendor/src/golang.org/x/crypto/openpgp/elgamal/elgamal.go
  41. 49
    0
      vendor/src/golang.org/x/crypto/openpgp/elgamal/elgamal_test.go
  42. 72
    0
      vendor/src/golang.org/x/crypto/openpgp/errors/errors.go
  43. 636
    0
      vendor/src/golang.org/x/crypto/openpgp/keys.go
  44. 419
    0
      vendor/src/golang.org/x/crypto/openpgp/keys_test.go
  45. 123
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/compressed.go
  46. 41
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/compressed_test.go
  47. 91
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/config.go
  48. 199
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/encrypted_key.go
  49. 146
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/encrypted_key_test.go
  50. 89
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/literal.go
  51. 143
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/ocfb.go
  52. 46
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/ocfb_test.go
  53. 73
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/one_pass_signature.go
  54. 162
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/opaque.go
  55. 67
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/opaque_test.go
  56. 537
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/packet.go
  57. 255
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/packet_test.go
  58. 380
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/private_key.go
  59. 270
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/private_key_test.go
  60. 748
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/public_key.go
  61. 202
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/public_key_test.go
  62. 279
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/public_key_v3.go
  63. 82
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/public_key_v3_test.go
  64. 76
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/reader.go
  65. 731
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/signature.go
  66. 78
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/signature_test.go
  67. 146
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/signature_v3.go
  68. 92
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/signature_v3_test.go
  69. 155
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go
  70. 117
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted_test.go
  71. 290
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go
  72. 123
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted_test.go
  73. 91
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/userattribute.go
  74. 109
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/userattribute_test.go
  75. 160
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/userid.go
  76. 87
    0
      vendor/src/golang.org/x/crypto/openpgp/packet/userid_test.go
  77. 442
    0
      vendor/src/golang.org/x/crypto/openpgp/read.go
  78. 613
    0
      vendor/src/golang.org/x/crypto/openpgp/read_test.go
  79. 273
    0
      vendor/src/golang.org/x/crypto/openpgp/s2k/s2k.go
  80. 137
    0
      vendor/src/golang.org/x/crypto/openpgp/s2k/s2k_test.go
  81. 378
    0
      vendor/src/golang.org/x/crypto/openpgp/write.go
  82. 273
    0
      vendor/src/golang.org/x/crypto/openpgp/write_test.go
  83. 3
    0
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/README.md
  84. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-0a00a25543e6d732dbf4e8e9fec55c8e65fc4e8d.tgz
  85. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-174be6bd4292c18160542ae6dc6704b877b8a01a.tgz
  86. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-21504f6d2cc2ef0c9d6ebb8802c7b49abae40c1a.tgz
  87. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-4870d54b5b04e43da8cf99ceec179d9675494af8.tgz
  88. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-4e7600af05c3356e8b142263e127b76f010facfc.tgz
  89. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-78c5fb882e76286d8201016cffee63ea7060a0c2.tgz
  90. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-7a725350b88b05ca03541b59dd0649fda7f521f2.tgz
  91. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-7cbde0ca02f13aedd5ec8b358ca17b1c0bf5ee64.tgz
  92. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-935e5ac17c41c309c356639816ea0694a568c484.tgz
  93. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-bf3fedcc8e20fd0dec9172987ceea0038d17b516.tgz
  94. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-c0c7c57ab1753ddbd26cc45322299ddd12842794.tgz
  95. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-df6781fd40b8f4911d70ce71f8387b991615cd6d.tgz
  96. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/git-e1580a78f7d36791249df76df8a2a2613d629902.tgz
  97. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/pack-0d3d824fb5c930e7e7e1f0f399f2976847d31fd3.idx
  98. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/pack-0d3d824fb5c930e7e7e1f0f399f2976847d31fd3.pack
  99. BIN
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/pack-0d9b6cfc261785837939aaede5986d7a7c212518.idx
  100. 0
    0
      vendor/src/gopkg.in/src-d/go-git-fixtures.v3/data/pack-0d9b6cfc261785837939aaede5986d7a7c212518.pack

+ 34
- 2
vendor/manifest View File

@@ -27,12 +27,24 @@
27 27
 			"branch": "master"
28 28
 		},
29 29
 		{
30
+			"importpath": "github.com/kevinburke/ssh_config",
31
+			"repository": "https://github.com/kevinburke/ssh_config",
32
+			"revision": "0ff8514904a8ebfcfb3c32ad73e1f8498a7f81b4",
33
+			"branch": "master"
34
+		},
35
+		{
30 36
 			"importpath": "github.com/mitchellh/go-homedir",
31 37
 			"repository": "https://github.com/mitchellh/go-homedir",
32 38
 			"revision": "b8bc1bf767474819792c23f32d8286a45736f1c6",
33 39
 			"branch": "master"
34 40
 		},
35 41
 		{
42
+			"importpath": "github.com/pelletier/go-buffruneio",
43
+			"repository": "https://github.com/pelletier/go-buffruneio",
44
+			"revision": "e2f66f8164ca709d4c21e815860afd2024e9b894",
45
+			"branch": "master"
46
+		},
47
+		{
36 48
 			"importpath": "github.com/rainycape/unidecode",
37 49
 			"repository": "https://github.com/rainycape/unidecode",
38 50
 			"revision": "cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c",
@@ -70,6 +82,13 @@
70 82
 			"branch": "master"
71 83
 		},
72 84
 		{
85
+			"importpath": "golang.org/x/crypto/cast5",
86
+			"repository": "https://go.googlesource.com/crypto",
87
+			"revision": "1875d0a70c90e57f11972aefd42276df65e895b9",
88
+			"branch": "master",
89
+			"path": "/cast5"
90
+		},
91
+		{
73 92
 			"importpath": "golang.org/x/crypto/curve25519",
74 93
 			"repository": "https://go.googlesource.com/crypto",
75 94
 			"revision": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8",
@@ -84,6 +103,13 @@
84 103
 			"path": "/ed25519"
85 104
 		},
86 105
 		{
106
+			"importpath": "golang.org/x/crypto/openpgp",
107
+			"repository": "https://go.googlesource.com/crypto",
108
+			"revision": "1875d0a70c90e57f11972aefd42276df65e895b9",
109
+			"branch": "master",
110
+			"path": "/openpgp"
111
+		},
112
+		{
87 113
 			"importpath": "golang.org/x/crypto/ssh",
88 114
 			"repository": "https://go.googlesource.com/crypto",
89 115
 			"revision": "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8",
@@ -143,10 +169,16 @@
143 169
 			"branch": "master"
144 170
 		},
145 171
 		{
172
+			"importpath": "gopkg.in/src-d/go-git-fixtures.v3",
173
+			"repository": "https://gopkg.in/src-d/go-git-fixtures.v3",
174
+			"revision": "a29d269c3be65e4d1b20c29133c74e0551e1aa5d",
175
+			"branch": "master"
176
+		},
177
+		{
146 178
 			"importpath": "gopkg.in/src-d/go-git.v4",
147 179
 			"repository": "https://gopkg.in/src-d/go-git.v4",
148
-			"revision": "f9879dd043f84936a1f8acb8a53b74332a7ae135",
149
-			"branch": "v4"
180
+			"revision": "e9247ce9c5ce12126f646ca3ddf0066e4829bd14",
181
+			"branch": "master"
150 182
 		},
151 183
 		{
152 184
 			"importpath": "gopkg.in/warnings.v0",

+ 2
- 0
vendor/src/github.com/kevinburke/ssh_config/AUTHORS.txt View File

@@ -0,0 +1,2 @@
1
+Kevin Burke <kev@inburke.com>
2
+Sergey Lukjanov <me@slukjanov.name>

+ 49
- 0
vendor/src/github.com/kevinburke/ssh_config/LICENSE View File

@@ -0,0 +1,49 @@
1
+Copyright (c) 2017 Kevin Burke.
2
+
3
+Permission is hereby granted, free of charge, to any person
4
+obtaining a copy of this software and associated documentation
5
+files (the "Software"), to deal in the Software without
6
+restriction, including without limitation the rights to use,
7
+copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+copies of the Software, and to permit persons to whom the
9
+Software is furnished to do so, subject to the following
10
+conditions:
11
+
12
+The above copyright notice and this permission notice shall be
13
+included in all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+===================
25
+
26
+The lexer and parser borrow heavily from github.com/pelletier/go-toml. The
27
+license for that project is copied below.
28
+
29
+The MIT License (MIT)
30
+
31
+Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton
32
+
33
+Permission is hereby granted, free of charge, to any person obtaining a copy
34
+of this software and associated documentation files (the "Software"), to deal
35
+in the Software without restriction, including without limitation the rights
36
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37
+copies of the Software, and to permit persons to whom the Software is
38
+furnished to do so, subject to the following conditions:
39
+
40
+The above copyright notice and this permission notice shall be included in all
41
+copies or substantial portions of the Software.
42
+
43
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49
+SOFTWARE.

+ 29
- 0
vendor/src/github.com/kevinburke/ssh_config/Makefile View File

@@ -0,0 +1,29 @@
1
+BUMP_VERSION := $(GOPATH)/bin/bump_version
2
+MEGACHECK := $(GOPATH)/bin/megacheck
3
+WRITE_MAILMAP := $(GOPATH)/bin/write_mailmap
4
+
5
+IGNORES := 'github.com/kevinburke/ssh_config/config.go:U1000 github.com/kevinburke/ssh_config/config.go:S1002 github.com/kevinburke/ssh_config/token.go:U1000'
6
+
7
+$(MEGACHECK):
8
+	go get honnef.co/go/tools/cmd/megacheck
9
+
10
+lint: $(MEGACHECK)
11
+	go vet ./...
12
+	$(MEGACHECK) --ignore=$(IGNORES) ./...
13
+
14
+test: lint
15
+	@# the timeout helps guard against infinite recursion
16
+	go test -timeout=50ms ./...
17
+
18
+$(BUMP_VERSION):
19
+	go get github.com/Shyp/bump_version
20
+
21
+release: $(BUMP_VERSION)
22
+	$(BUMP_VERSION) minor config.go
23
+
24
+force: ;
25
+
26
+AUTHORS.txt: force | $(WRITE_MAILMAP)
27
+	$(WRITE_MAILMAP) > AUTHORS.txt
28
+
29
+authors: AUTHORS.txt

+ 81
- 0
vendor/src/github.com/kevinburke/ssh_config/README.md View File

@@ -0,0 +1,81 @@
1
+# ssh_config
2
+
3
+This is a Go parser for `ssh_config` files. Importantly, this parser attempts
4
+to preserve comments in a given file, so you can manipulate a `ssh_config` file
5
+from a program, if your heart desires.
6
+
7
+It's designed to be used with the excellent
8
+[x/crypto/ssh](https://golang.org/x/crypto/ssh) package, which handles SSH
9
+negotiation but isn't very easy to configure.
10
+
11
+The `ssh_config` `Get()` and `GetStrict()` functions will attempt to read values
12
+from `$HOME/.ssh/config` and fall back to `/etc/ssh/ssh_config`. The first
13
+argument is the host name to match on, and the second argument is the key you
14
+want to retrieve.
15
+
16
+```go
17
+port := ssh_config.Get("myhost", "Port")
18
+```
19
+
20
+You can also load a config file and read values from it.
21
+
22
+```go
23
+var config = `
24
+Host *.test
25
+  Compression yes
26
+`
27
+
28
+cfg, err := ssh_config.Decode(strings.NewReader(config))
29
+fmt.Println(cfg.Get("example.test", "Port"))
30
+```
31
+
32
+Some SSH arguments have default values - for example, the default value for
33
+`KeyboardAuthentication` is `"yes"`. If you call Get(), and no value for the
34
+given Host/keyword pair exists in the config, we'll return a default for the
35
+keyword if one exists.
36
+
37
+### Manipulating SSH config files
38
+
39
+Here's how you can manipulate an SSH config file, and then write it back to
40
+disk.
41
+
42
+```go
43
+f, _ := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "config"))
44
+cfg, _ := ssh_config.Decode(f)
45
+for _, host := range cfg.Hosts {
46
+    fmt.Println("patterns:", host.Patterns)
47
+    for _, node := range host.Nodes {
48
+        // Manipulate the nodes as you see fit, or use a type switch to
49
+        // distinguish between Empty, KV, and Include nodes.
50
+        fmt.Println(node.String())
51
+    }
52
+}
53
+
54
+// Print the config to stdout:
55
+fmt.Println(cfg.String())
56
+```
57
+
58
+## Spec compliance
59
+
60
+Wherever possible we try to implement the specification as documented in
61
+the `ssh_config` manpage. Unimplemented features should be present in the
62
+[issues][issues] list.
63
+
64
+Notably, the `Match` directive is currently unsupported.
65
+
66
+[issues]: https://github.com/kevinburke/ssh_config/issues
67
+
68
+## Errata
69
+
70
+This is the second [comment-preserving configuration parser][blog] I've written, after
71
+[an /etc/hosts parser][hostsfile]. Eventually, I will write one for every Linux
72
+file format.
73
+
74
+[blog]: https://kev.inburke.com/kevin/more-comment-preserving-configuration-parsers/
75
+[hostsfile]: https://github.com/kevinburke/hostsfile
76
+
77
+## Donating
78
+
79
+Donations free up time to make improvements to the library, and respond to
80
+bug reports. You can send donations via Paypal's "Send Money" feature to
81
+kev@inburke.com. Donations are not tax deductible in the USA.

+ 639
- 0
vendor/src/github.com/kevinburke/ssh_config/config.go View File

@@ -0,0 +1,639 @@
1
+// Package ssh_config provides tools for manipulating SSH config files.
2
+//
3
+// Importantly, this parser attempts to preserve comments in a given file, so
4
+// you can manipulate a `ssh_config` file from a program, if your heart desires.
5
+//
6
+// The Get() and GetStrict() functions will attempt to read values from
7
+// $HOME/.ssh/config, falling back to /etc/ssh/ssh_config. The first argument is
8
+// the host name to match on ("example.com"), and the second argument is the key
9
+// you want to retrieve ("Port"). The keywords are case insensitive.
10
+//
11
+// 		port := ssh_config.Get("myhost", "Port")
12
+//
13
+// You can also manipulate an SSH config file and then print it or write it back
14
+// to disk.
15
+//
16
+//	f, _ := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "config"))
17
+//	cfg, _ := ssh_config.Decode(f)
18
+//	for _, host := range cfg.Hosts {
19
+//		fmt.Println("patterns:", host.Patterns)
20
+//		for _, node := range host.Nodes {
21
+//			fmt.Println(node.String())
22
+//		}
23
+//	}
24
+//
25
+//	// Write the cfg back to disk:
26
+//	fmt.Println(cfg.String())
27
+//
28
+// BUG: the Match directive is currently unsupported; parsing a config with
29
+// a Match directive will trigger an error.
30
+package ssh_config
31
+
32
+import (
33
+	"bytes"
34
+	"errors"
35
+	"fmt"
36
+	"io"
37
+	"os"
38
+	osuser "os/user"
39
+	"path/filepath"
40
+	"regexp"
41
+	"runtime"
42
+	"strings"
43
+	"sync"
44
+)
45
+
46
+const version = "0.3"
47
+
48
+type configFinder func() string
49
+
50
+// UserSettings checks ~/.ssh and /etc/ssh for configuration files. The config
51
+// files are parsed and cached the first time Get() or GetStrict() is called.
52
+type UserSettings struct {
53
+	IgnoreErrors       bool
54
+	systemConfig       *Config
55
+	systemConfigFinder configFinder
56
+	userConfig         *Config
57
+	userConfigFinder   configFinder
58
+	loadConfigs        sync.Once
59
+	onceErr            error
60
+}
61
+
62
+func homedir() string {
63
+	user, err := osuser.Current()
64
+	if err == nil {
65
+		return user.HomeDir
66
+	} else {
67
+		return os.Getenv("HOME")
68
+	}
69
+}
70
+
71
+func userConfigFinder() string {
72
+	return filepath.Join(homedir(), ".ssh", "config")
73
+}
74
+
75
+// DefaultUserSettings is the default UserSettings and is used by Get and
76
+// GetStrict. It checks both $HOME/.ssh/config and /etc/ssh/ssh_config for keys,
77
+// and it will return parse errors (if any) instead of swallowing them.
78
+var DefaultUserSettings = &UserSettings{
79
+	IgnoreErrors:       false,
80
+	systemConfigFinder: systemConfigFinder,
81
+	userConfigFinder:   userConfigFinder,
82
+}
83
+
84
+func systemConfigFinder() string {
85
+	return filepath.Join("/", "etc", "ssh", "ssh_config")
86
+}
87
+
88
+func findVal(c *Config, alias, key string) (string, error) {
89
+	if c == nil {
90
+		return "", nil
91
+	}
92
+	val, err := c.Get(alias, key)
93
+	if err != nil || val == "" {
94
+		return "", err
95
+	}
96
+	if err := validate(key, val); err != nil {
97
+		return "", err
98
+	}
99
+	return val, nil
100
+}
101
+
102
+// Get finds the first value for key within a declaration that matches the
103
+// alias. Get returns the empty string if no value was found, or if IgnoreErrors
104
+// is false and we could not parse the configuration file. Use GetStrict to
105
+// disambiguate the latter cases.
106
+//
107
+// The match for key is case insensitive.
108
+//
109
+// Get is a wrapper around DefaultUserSettings.Get.
110
+func Get(alias, key string) string {
111
+	return DefaultUserSettings.Get(alias, key)
112
+}
113
+
114
+// GetStrict finds the first value for key within a declaration that matches the
115
+// alias. If key has a default value and no matching configuration is found, the
116
+// default will be returned. For more information on default values and the way
117
+// patterns are matched, see the manpage for ssh_config.
118
+//
119
+// error will be non-nil if and only if a user's configuration file or the
120
+// system configuration file could not be parsed, and u.IgnoreErrors is false.
121
+//
122
+// GetStrict is a wrapper around DefaultUserSettings.GetStrict.
123
+func GetStrict(alias, key string) (string, error) {
124
+	return DefaultUserSettings.GetStrict(alias, key)
125
+}
126
+
127
+// Get finds the first value for key within a declaration that matches the
128
+// alias. Get returns the empty string if no value was found, or if IgnoreErrors
129
+// is false and we could not parse the configuration file. Use GetStrict to
130
+// disambiguate the latter cases.
131
+//
132
+// The match for key is case insensitive.
133
+func (u *UserSettings) Get(alias, key string) string {
134
+	val, err := u.GetStrict(alias, key)
135
+	if err != nil {
136
+		return ""
137
+	}
138
+	return val
139
+}
140
+
141
+// GetStrict finds the first value for key within a declaration that matches the
142
+// alias. If key has a default value and no matching configuration is found, the
143
+// default will be returned. For more information on default values and the way
144
+// patterns are matched, see the manpage for ssh_config.
145
+//
146
+// error will be non-nil if and only if a user's configuration file or the
147
+// system configuration file could not be parsed, and u.IgnoreErrors is false.
148
+func (u *UserSettings) GetStrict(alias, key string) (string, error) {
149
+	u.loadConfigs.Do(func() {
150
+		// can't parse user file, that's ok.
151
+		var filename string
152
+		if u.userConfigFinder == nil {
153
+			filename = userConfigFinder()
154
+		} else {
155
+			filename = u.userConfigFinder()
156
+		}
157
+		var err error
158
+		u.userConfig, err = parseFile(filename)
159
+		if err != nil && os.IsNotExist(err) == false {
160
+			u.onceErr = err
161
+			return
162
+		}
163
+		if u.systemConfigFinder == nil {
164
+			filename = systemConfigFinder()
165
+		} else {
166
+			filename = u.systemConfigFinder()
167
+		}
168
+		u.systemConfig, err = parseFile(filename)
169
+		if err != nil && os.IsNotExist(err) == false {
170
+			u.onceErr = err
171
+			return
172
+		}
173
+	})
174
+	if u.onceErr != nil && u.IgnoreErrors == false {
175
+		return "", u.onceErr
176
+	}
177
+	val, err := findVal(u.userConfig, alias, key)
178
+	if err != nil || val != "" {
179
+		return val, err
180
+	}
181
+	val2, err2 := findVal(u.systemConfig, alias, key)
182
+	if err2 != nil || val2 != "" {
183
+		return val2, err2
184
+	}
185
+	return Default(key), nil
186
+}
187
+
188
+func parseFile(filename string) (*Config, error) {
189
+	return parseWithDepth(filename, 0)
190
+}
191
+
192
+func parseWithDepth(filename string, depth uint8) (*Config, error) {
193
+	f, err := os.Open(filename)
194
+	if err != nil {
195
+		return nil, err
196
+	}
197
+	defer f.Close()
198
+	return decode(f, isSystem(filename), depth)
199
+}
200
+
201
+func isSystem(filename string) bool {
202
+	// TODO i'm not sure this is the best way to detect a system repo
203
+	return strings.HasPrefix(filepath.Clean(filename), "/etc/ssh")
204
+}
205
+
206
+// Decode reads r into a Config, or returns an error if r could not be parsed as
207
+// an SSH config file.
208
+func Decode(r io.Reader) (*Config, error) {
209
+	return decode(r, false, 0)
210
+}
211
+
212
+func decode(r io.Reader, system bool, depth uint8) (c *Config, err error) {
213
+	defer func() {
214
+		if r := recover(); r != nil {
215
+			if _, ok := r.(runtime.Error); ok {
216
+				panic(r)
217
+			}
218
+			if e, ok := r.(error); ok && e == ErrDepthExceeded {
219
+				err = e
220
+				return
221
+			}
222
+			err = errors.New(r.(string))
223
+		}
224
+	}()
225
+
226
+	c = parseSSH(lexSSH(r), system, depth)
227
+	return c, err
228
+}
229
+
230
+// Config represents an SSH config file.
231
+type Config struct {
232
+	// A list of hosts to match against. The file begins with an implicit
233
+	// "Host *" declaration matching all hosts.
234
+	Hosts    []*Host
235
+	depth    uint8
236
+	position Position
237
+}
238
+
239
+// Get finds the first value in the configuration that matches the alias and
240
+// contains key. Get returns the empty string if no value was found, or if the
241
+// Config contains an invalid conditional Include value.
242
+//
243
+// The match for key is case insensitive.
244
+func (c *Config) Get(alias, key string) (string, error) {
245
+	lowerKey := strings.ToLower(key)
246
+	for _, host := range c.Hosts {
247
+		if !host.Matches(alias) {
248
+			continue
249
+		}
250
+		for _, node := range host.Nodes {
251
+			switch t := node.(type) {
252
+			case *Empty:
253
+				continue
254
+			case *KV:
255
+				// "keys are case insensitive" per the spec
256
+				lkey := strings.ToLower(t.Key)
257
+				if lkey == "match" {
258
+					panic("can't handle Match directives")
259
+				}
260
+				if lkey == lowerKey {
261
+					return t.Value, nil
262
+				}
263
+			case *Include:
264
+				val := t.Get(alias, key)
265
+				if val != "" {
266
+					return val, nil
267
+				}
268
+			default:
269
+				return "", fmt.Errorf("unknown Node type %v", t)
270
+			}
271
+		}
272
+	}
273
+	return "", nil
274
+}
275
+
276
+// String returns a string representation of the Config file.
277
+func (c Config) String() string {
278
+	return marshal(c).String()
279
+}
280
+
281
+func (c Config) MarshalText() ([]byte, error) {
282
+	return marshal(c).Bytes(), nil
283
+}
284
+
285
+func marshal(c Config) *bytes.Buffer {
286
+	var buf bytes.Buffer
287
+	for i := range c.Hosts {
288
+		buf.WriteString(c.Hosts[i].String())
289
+	}
290
+	return &buf
291
+}
292
+
293
+// Pattern is a pattern in a Host declaration. Patterns are read-only values;
294
+// create a new one with NewPattern().
295
+type Pattern struct {
296
+	str   string // Its appearance in the file, not the value that gets compiled.
297
+	regex *regexp.Regexp
298
+	not   bool // True if this is a negated match
299
+}
300
+
301
+// String prints the string representation of the pattern.
302
+func (p Pattern) String() string {
303
+	return p.str
304
+}
305
+
306
+// Copied from regexp.go with * and ? removed.
307
+var specialBytes = []byte(`\.+()|[]{}^$`)
308
+
309
+func special(b byte) bool {
310
+	return bytes.IndexByte(specialBytes, b) >= 0
311
+}
312
+
313
+// NewPattern creates a new Pattern for matching hosts. NewPattern("*") creates
314
+// a Pattern that matches all hosts.
315
+//
316
+// From the manpage, a pattern consists of zero or more non-whitespace
317
+// characters, `*' (a wildcard that matches zero or more characters), or `?' (a
318
+// wildcard that matches exactly one character). For example, to specify a set
319
+// of declarations for any host in the ".co.uk" set of domains, the following
320
+// pattern could be used:
321
+//
322
+//	Host *.co.uk
323
+//
324
+// The following pattern would match any host in the 192.168.0.[0-9] network range:
325
+//
326
+//	Host 192.168.0.?
327
+func NewPattern(s string) (*Pattern, error) {
328
+	if s == "" {
329
+		return nil, errors.New("ssh_config: empty pattern")
330
+	}
331
+	negated := false
332
+	if s[0] == '!' {
333
+		negated = true
334
+		s = s[1:]
335
+	}
336
+	var buf bytes.Buffer
337
+	buf.WriteByte('^')
338
+	for i := 0; i < len(s); i++ {
339
+		// A byte loop is correct because all metacharacters are ASCII.
340
+		switch b := s[i]; b {
341
+		case '*':
342
+			buf.WriteString(".*")
343
+		case '?':
344
+			buf.WriteString(".?")
345
+		default:
346
+			// borrowing from QuoteMeta here.
347
+			if special(b) {
348
+				buf.WriteByte('\\')
349
+			}
350
+			buf.WriteByte(b)
351
+		}
352
+	}
353
+	buf.WriteByte('$')
354
+	r, err := regexp.Compile(buf.String())
355
+	if err != nil {
356
+		return nil, err
357
+	}
358
+	return &Pattern{str: s, regex: r, not: negated}, nil
359
+}
360
+
361
+// Host describes a Host directive and the keywords that follow it.
362
+type Host struct {
363
+	// A list of host patterns that should match this host.
364
+	Patterns []*Pattern
365
+	// A Node is either a key/value pair or a comment line.
366
+	Nodes []Node
367
+	// EOLComment is the comment (if any) terminating the Host line.
368
+	EOLComment   string
369
+	hasEquals    bool
370
+	leadingSpace uint16 // TODO: handle spaces vs tabs here.
371
+	// The file starts with an implicit "Host *" declaration.
372
+	implicit bool
373
+}
374
+
375
+// Matches returns true if the Host matches for the given alias. For
376
+// a description of the rules that provide a match, see the manpage for
377
+// ssh_config.
378
+func (h *Host) Matches(alias string) bool {
379
+	found := false
380
+	for i := range h.Patterns {
381
+		if h.Patterns[i].regex.MatchString(alias) {
382
+			if h.Patterns[i].not == true {
383
+				// Negated match. "A pattern entry may be negated by prefixing
384
+				// it with an exclamation mark (`!'). If a negated entry is
385
+				// matched, then the Host entry is ignored, regardless of
386
+				// whether any other patterns on the line match. Negated matches
387
+				// are therefore useful to provide exceptions for wildcard
388
+				// matches."
389
+				return false
390
+			}
391
+			found = true
392
+		}
393
+	}
394
+	return found
395
+}
396
+
397
+// String prints h as it would appear in a config file. Minor tweaks may be
398
+// present in the whitespace in the printed file.
399
+func (h *Host) String() string {
400
+	var buf bytes.Buffer
401
+	if h.implicit == false {
402
+		buf.WriteString(strings.Repeat(" ", int(h.leadingSpace)))
403
+		buf.WriteString("Host")
404
+		if h.hasEquals {
405
+			buf.WriteString(" = ")
406
+		} else {
407
+			buf.WriteString(" ")
408
+		}
409
+		for i, pat := range h.Patterns {
410
+			buf.WriteString(pat.String())
411
+			if i < len(h.Patterns)-1 {
412
+				buf.WriteString(" ")
413
+			}
414
+		}
415
+		if h.EOLComment != "" {
416
+			buf.WriteString(" #")
417
+			buf.WriteString(h.EOLComment)
418
+		}
419
+		buf.WriteByte('\n')
420
+	}
421
+	for i := range h.Nodes {
422
+		buf.WriteString(h.Nodes[i].String())
423
+		buf.WriteByte('\n')
424
+	}
425
+	return buf.String()
426
+}
427
+
428
+// Node represents a line in a Config.
429
+type Node interface {
430
+	Pos() Position
431
+	String() string
432
+}
433
+
434
+// KV is a line in the config file that contains a key, a value, and possibly
435
+// a comment.
436
+type KV struct {
437
+	Key          string
438
+	Value        string
439
+	Comment      string
440
+	hasEquals    bool
441
+	leadingSpace uint16 // Space before the key. TODO handle spaces vs tabs.
442
+	position     Position
443
+}
444
+
445
+// Pos returns k's Position.
446
+func (k *KV) Pos() Position {
447
+	return k.position
448
+}
449
+
450
+// String prints k as it was parsed in the config file. There may be slight
451
+// changes to the whitespace between values.
452
+func (k *KV) String() string {
453
+	if k == nil {
454
+		return ""
455
+	}
456
+	equals := " "
457
+	if k.hasEquals {
458
+		equals = " = "
459
+	}
460
+	line := fmt.Sprintf("%s%s%s%s", strings.Repeat(" ", int(k.leadingSpace)), k.Key, equals, k.Value)
461
+	if k.Comment != "" {
462
+		line += " #" + k.Comment
463
+	}
464
+	return line
465
+}
466
+
467
+// Empty is a line in the config file that contains only whitespace or comments.
468
+type Empty struct {
469
+	Comment      string
470
+	leadingSpace uint16 // TODO handle spaces vs tabs.
471
+	position     Position
472
+}
473
+
474
+// Pos returns e's Position.
475
+func (e *Empty) Pos() Position {
476
+	return e.position
477
+}
478
+
479
+// String prints e as it was parsed in the config file.
480
+func (e *Empty) String() string {
481
+	if e == nil {
482
+		return ""
483
+	}
484
+	if e.Comment == "" {
485
+		return ""
486
+	}
487
+	return fmt.Sprintf("%s#%s", strings.Repeat(" ", int(e.leadingSpace)), e.Comment)
488
+}
489
+
490
+// Include holds the result of an Include directive, including the config files
491
+// that have been parsed as part of that directive. At most 5 levels of Include
492
+// statements will be parsed.
493
+type Include struct {
494
+	// Comment is the contents of any comment at the end of the Include
495
+	// statement.
496
+	Comment string
497
+	parsed  bool
498
+	// an include directive can include several different files, and wildcards
499
+	directives []string
500
+
501
+	mu sync.Mutex
502
+	// 1:1 mapping between matches and keys in files array; matches preserves
503
+	// ordering
504
+	matches []string
505
+	// actual filenames are listed here
506
+	files        map[string]*Config
507
+	leadingSpace uint16
508
+	position     Position
509
+	depth        uint8
510
+	hasEquals    bool
511
+}
512
+
513
+const maxRecurseDepth = 5
514
+
515
+// ErrDepthExceeded is returned if too many Include directives are parsed.
516
+// Usually this indicates a recursive loop (an Include directive pointing to the
517
+// file it contains).
518
+var ErrDepthExceeded = errors.New("ssh_config: max recurse depth exceeded")
519
+
520
+func removeDups(arr []string) []string {
521
+	// Use map to record duplicates as we find them.
522
+	encountered := make(map[string]bool, len(arr))
523
+	result := make([]string, 0)
524
+
525
+	for v := range arr {
526
+		if encountered[arr[v]] == false {
527
+			encountered[arr[v]] = true
528
+			result = append(result, arr[v])
529
+		}
530
+	}
531
+	return result
532
+}
533
+
534
+// NewInclude creates a new Include with a list of file globs to include.
535
+// Configuration files are parsed greedily (e.g. as soon as this function runs).
536
+// Any error encountered while parsing nested configuration files will be
537
+// returned.
538
+func NewInclude(directives []string, hasEquals bool, pos Position, comment string, system bool, depth uint8) (*Include, error) {
539
+	if depth > maxRecurseDepth {
540
+		return nil, ErrDepthExceeded
541
+	}
542
+	inc := &Include{
543
+		Comment:      comment,
544
+		directives:   directives,
545
+		files:        make(map[string]*Config),
546
+		position:     pos,
547
+		leadingSpace: uint16(pos.Col) - 1,
548
+		depth:        depth,
549
+		hasEquals:    hasEquals,
550
+	}
551
+	// no need for inc.mu.Lock() since nothing else can access this inc
552
+	matches := make([]string, 0)
553
+	for i := range directives {
554
+		var path string
555
+		if filepath.IsAbs(directives[i]) {
556
+			path = directives[i]
557
+		} else if system {
558
+			path = filepath.Join("/etc/ssh", directives[i])
559
+		} else {
560
+			path = filepath.Join(homedir(), ".ssh", directives[i])
561
+		}
562
+		theseMatches, err := filepath.Glob(path)
563
+		if err != nil {
564
+			return nil, err
565
+		}
566
+		matches = append(matches, theseMatches...)
567
+	}
568
+	matches = removeDups(matches)
569
+	inc.matches = matches
570
+	for i := range matches {
571
+		config, err := parseWithDepth(matches[i], depth)
572
+		if err != nil {
573
+			return nil, err
574
+		}
575
+		inc.files[matches[i]] = config
576
+	}
577
+	return inc, nil
578
+}
579
+
580
+// Pos returns the position of the Include directive in the larger file.
581
+func (i *Include) Pos() Position {
582
+	return i.position
583
+}
584
+
585
+// Get finds the first value in the Include statement matching the alias and the
586
+// given key.
587
+func (inc *Include) Get(alias, key string) string {
588
+	inc.mu.Lock()
589
+	defer inc.mu.Unlock()
590
+	// TODO: we search files in any order which is not correct
591
+	for i := range inc.matches {
592
+		cfg := inc.files[inc.matches[i]]
593
+		if cfg == nil {
594
+			panic("nil cfg")
595
+		}
596
+		val, err := cfg.Get(alias, key)
597
+		if err == nil && val != "" {
598
+			return val
599
+		}
600
+	}
601
+	return ""
602
+}
603
+
604
+// String prints out a string representation of this Include directive. Note
605
+// included Config files are not printed as part of this representation.
606
+func (inc *Include) String() string {
607
+	equals := " "
608
+	if inc.hasEquals {
609
+		equals = " = "
610
+	}
611
+	line := fmt.Sprintf("%sInclude%s%s", strings.Repeat(" ", int(inc.leadingSpace)), equals, strings.Join(inc.directives, " "))
612
+	if inc.Comment != "" {
613
+		line += " #" + inc.Comment
614
+	}
615
+	return line
616
+}
617
+
618
+var matchAll *Pattern
619
+
620
+func init() {
621
+	var err error
622
+	matchAll, err = NewPattern("*")
623
+	if err != nil {
624
+		panic(err)
625
+	}
626
+}
627
+
628
+func newConfig() *Config {
629
+	return &Config{
630
+		Hosts: []*Host{
631
+			&Host{
632
+				implicit: true,
633
+				Patterns: []*Pattern{matchAll},
634
+				Nodes:    make([]Node, 0),
635
+			},
636
+		},
637
+		depth: 0,
638
+	}
639
+}

+ 340
- 0
vendor/src/github.com/kevinburke/ssh_config/config_test.go View File

@@ -0,0 +1,340 @@
1
+package ssh_config
2
+
3
+import (
4
+	"bytes"
5
+	"io/ioutil"
6
+	"log"
7
+	"os"
8
+	"path/filepath"
9
+	"strings"
10
+	"testing"
11
+)
12
+
13
+func loadFile(t *testing.T, filename string) []byte {
14
+	data, err := ioutil.ReadFile(filename)
15
+	if err != nil {
16
+		t.Fatal(err)
17
+	}
18
+	return data
19
+}
20
+
21
+var files = []string{"testdata/config1", "testdata/config2"}
22
+
23
+func TestDecode(t *testing.T) {
24
+	for _, filename := range files {
25
+		data := loadFile(t, filename)
26
+		cfg, err := Decode(bytes.NewReader(data))
27
+		if err != nil {
28
+			t.Fatal(err)
29
+		}
30
+		out := cfg.String()
31
+		if out != string(data) {
32
+			t.Errorf("out != data: out: %q\ndata: %q", out, string(data))
33
+		}
34
+	}
35
+}
36
+
37
+func testConfigFinder(filename string) func() string {
38
+	return func() string { return filename }
39
+}
40
+
41
+func nullConfigFinder() string {
42
+	return ""
43
+}
44
+
45
+func TestGet(t *testing.T) {
46
+	us := &UserSettings{
47
+		userConfigFinder: testConfigFinder("testdata/config1"),
48
+	}
49
+
50
+	val := us.Get("wap", "User")
51
+	if val != "root" {
52
+		t.Errorf("expected to find User root, got %q", val)
53
+	}
54
+}
55
+
56
+func TestGetWithDefault(t *testing.T) {
57
+	us := &UserSettings{
58
+		userConfigFinder: testConfigFinder("testdata/config1"),
59
+	}
60
+
61
+	val, err := us.GetStrict("wap", "PasswordAuthentication")
62
+	if err != nil {
63
+		t.Fatalf("expected nil err, got %v", err)
64
+	}
65
+	if val != "yes" {
66
+		t.Errorf("expected to get PasswordAuthentication yes, got %q", val)
67
+	}
68
+}
69
+
70
+func TestGetInvalidPort(t *testing.T) {
71
+	us := &UserSettings{
72
+		userConfigFinder: testConfigFinder("testdata/invalid-port"),
73
+	}
74
+
75
+	val, err := us.GetStrict("test.test", "Port")
76
+	if err == nil {
77
+		t.Fatalf("expected non-nil err, got nil")
78
+	}
79
+	if val != "" {
80
+		t.Errorf("expected to get '' for val, got %q", val)
81
+	}
82
+	if err.Error() != `ssh_config: strconv.ParseUint: parsing "notanumber": invalid syntax` {
83
+		t.Errorf("wrong error: got %v", err)
84
+	}
85
+}
86
+
87
+func TestGetNotFoundNoDefault(t *testing.T) {
88
+	us := &UserSettings{
89
+		userConfigFinder: testConfigFinder("testdata/config1"),
90
+	}
91
+
92
+	val, err := us.GetStrict("wap", "CanonicalDomains")
93
+	if err != nil {
94
+		t.Fatalf("expected nil err, got %v", err)
95
+	}
96
+	if val != "" {
97
+		t.Errorf("expected to get CanonicalDomains '', got %q", val)
98
+	}
99
+}
100
+
101
+func TestGetWildcard(t *testing.T) {
102
+	us := &UserSettings{
103
+		userConfigFinder: testConfigFinder("testdata/config3"),
104
+	}
105
+
106
+	val := us.Get("bastion.stage.i.us.example.net", "Port")
107
+	if val != "22" {
108
+		t.Errorf("expected to find Port 22, got %q", val)
109
+	}
110
+
111
+	val = us.Get("bastion.net", "Port")
112
+	if val != "25" {
113
+		t.Errorf("expected to find Port 24, got %q", val)
114
+	}
115
+
116
+	val = us.Get("10.2.3.4", "Port")
117
+	if val != "23" {
118
+		t.Errorf("expected to find Port 23, got %q", val)
119
+	}
120
+	val = us.Get("101.2.3.4", "Port")
121
+	if val != "25" {
122
+		t.Errorf("expected to find Port 24, got %q", val)
123
+	}
124
+	val = us.Get("20.20.20.4", "Port")
125
+	if val != "24" {
126
+		t.Errorf("expected to find Port 24, got %q", val)
127
+	}
128
+	val = us.Get("20.20.20.20", "Port")
129
+	if val != "25" {
130
+		t.Errorf("expected to find Port 25, got %q", val)
131
+	}
132
+}
133
+
134
+func TestGetExtraSpaces(t *testing.T) {
135
+	us := &UserSettings{
136
+		userConfigFinder: testConfigFinder("testdata/extraspace"),
137
+	}
138
+
139
+	val := us.Get("test.test", "Port")
140
+	if val != "1234" {
141
+		t.Errorf("expected to find Port 1234, got %q", val)
142
+	}
143
+}
144
+
145
+func TestGetCaseInsensitive(t *testing.T) {
146
+	us := &UserSettings{
147
+		userConfigFinder: testConfigFinder("testdata/config1"),
148
+	}
149
+
150
+	val := us.Get("wap", "uSER")
151
+	if val != "root" {
152
+		t.Errorf("expected to find User root, got %q", val)
153
+	}
154
+}
155
+
156
+func TestGetEmpty(t *testing.T) {
157
+	us := &UserSettings{
158
+		userConfigFinder:   nullConfigFinder,
159
+		systemConfigFinder: nullConfigFinder,
160
+	}
161
+	val, err := us.GetStrict("wap", "User")
162
+	if err != nil {
163
+		t.Errorf("expected nil error, got %v", err)
164
+	}
165
+	if val != "" {
166
+		t.Errorf("expected to get empty string, got %q", val)
167
+	}
168
+}
169
+
170
+func TestGetEqsign(t *testing.T) {
171
+	us := &UserSettings{
172
+		userConfigFinder: testConfigFinder("testdata/eqsign"),
173
+	}
174
+
175
+	val := us.Get("test.test", "Port")
176
+	if val != "1234" {
177
+		t.Errorf("expected to find Port 1234, got %q", val)
178
+	}
179
+	val = us.Get("test.test", "Port2")
180
+	if val != "5678" {
181
+		t.Errorf("expected to find Port2 5678, got %q", val)
182
+	}
183
+}
184
+
185
+var includeFile = []byte(`
186
+# This host should not exist, so we can use it for test purposes / it won't
187
+# interfere with any other configurations.
188
+Host kevinburke.ssh_config.test.example.com
189
+    Port 4567
190
+`)
191
+
192
+func TestInclude(t *testing.T) {
193
+	if testing.Short() {
194
+		t.Skip("skipping fs write in short mode")
195
+	}
196
+	testPath := filepath.Join(homedir(), ".ssh", "kevinburke-ssh-config-test-file")
197
+	err := ioutil.WriteFile(testPath, includeFile, 0644)
198
+	if err != nil {
199
+		t.Skipf("couldn't write SSH config file: %v", err.Error())
200
+	}
201
+	defer os.Remove(testPath)
202
+	us := &UserSettings{
203
+		userConfigFinder: testConfigFinder("testdata/include"),
204
+	}
205
+	val := us.Get("kevinburke.ssh_config.test.example.com", "Port")
206
+	if val != "4567" {
207
+		t.Errorf("expected to find Port=4567 in included file, got %q", val)
208
+	}
209
+}
210
+
211
+func TestIncludeSystem(t *testing.T) {
212
+	if testing.Short() {
213
+		t.Skip("skipping fs write in short mode")
214
+	}
215
+	testPath := filepath.Join("/", "etc", "ssh", "kevinburke-ssh-config-test-file")
216
+	err := ioutil.WriteFile(testPath, includeFile, 0644)
217
+	if err != nil {
218
+		t.Skipf("couldn't write SSH config file: %v", err.Error())
219
+	}
220
+	defer os.Remove(testPath)
221
+	us := &UserSettings{
222
+		systemConfigFinder: testConfigFinder("testdata/include"),
223
+	}
224
+	val := us.Get("kevinburke.ssh_config.test.example.com", "Port")
225
+	if val != "4567" {
226
+		t.Errorf("expected to find Port=4567 in included file, got %q", val)
227
+	}
228
+}
229
+
230
+var recursiveIncludeFile = []byte(`
231
+Host kevinburke.ssh_config.test.example.com
232
+	Include kevinburke-ssh-config-recursive-include
233
+`)
234
+
235
+func TestIncludeRecursive(t *testing.T) {
236
+	if testing.Short() {
237
+		t.Skip("skipping fs write in short mode")
238
+	}
239
+	testPath := filepath.Join(homedir(), ".ssh", "kevinburke-ssh-config-recursive-include")
240
+	err := ioutil.WriteFile(testPath, recursiveIncludeFile, 0644)
241
+	if err != nil {
242
+		t.Skipf("couldn't write SSH config file: %v", err.Error())
243
+	}
244
+	defer os.Remove(testPath)
245
+	us := &UserSettings{
246
+		userConfigFinder: testConfigFinder("testdata/include-recursive"),
247
+	}
248
+	val, err := us.GetStrict("kevinburke.ssh_config.test.example.com", "Port")
249
+	if err != ErrDepthExceeded {
250
+		t.Errorf("Recursive include: expected ErrDepthExceeded, got %v", err)
251
+	}
252
+	if val != "" {
253
+		t.Errorf("non-empty string value %s", val)
254
+	}
255
+}
256
+
257
+func TestIncludeString(t *testing.T) {
258
+	if testing.Short() {
259
+		t.Skip("skipping fs write in short mode")
260
+	}
261
+	data, err := ioutil.ReadFile("testdata/include")
262
+	if err != nil {
263
+		log.Fatal(err)
264
+	}
265
+	c, err := Decode(bytes.NewReader(data))
266
+	if err != nil {
267
+		t.Fatal(err)
268
+	}
269
+	s := c.String()
270
+	if s != string(data) {
271
+		t.Errorf("mismatch: got %q\nwant %q", s, string(data))
272
+	}
273
+}
274
+
275
+var matchTests = []struct {
276
+	in    []string
277
+	alias string
278
+	want  bool
279
+}{
280
+	{[]string{"*"}, "any.test", true},
281
+	{[]string{"a", "b", "*", "c"}, "any.test", true},
282
+	{[]string{"a", "b", "c"}, "any.test", false},
283
+	{[]string{"any.test"}, "any1test", false},
284
+	{[]string{"192.168.0.?"}, "192.168.0.1", true},
285
+	{[]string{"192.168.0.?"}, "192.168.0.10", false},
286
+	{[]string{"*.co.uk"}, "bbc.co.uk", true},
287
+	{[]string{"*.co.uk"}, "subdomain.bbc.co.uk", true},
288
+	{[]string{"*.*.co.uk"}, "bbc.co.uk", false},
289
+	{[]string{"*.*.co.uk"}, "subdomain.bbc.co.uk", true},
290
+	{[]string{"*.example.com", "!*.dialup.example.com", "foo.dialup.example.com"}, "foo.dialup.example.com", false},
291
+	{[]string{"test.*", "!test.host"}, "test.host", false},
292
+}
293
+
294
+func TestMatches(t *testing.T) {
295
+	for _, tt := range matchTests {
296
+		patterns := make([]*Pattern, len(tt.in))
297
+		for i := range tt.in {
298
+			pat, err := NewPattern(tt.in[i])
299
+			if err != nil {
300
+				t.Fatalf("error compiling pattern %s: %v", tt.in[i], err)
301
+			}
302
+			patterns[i] = pat
303
+		}
304
+		host := &Host{
305
+			Patterns: patterns,
306
+		}
307
+		got := host.Matches(tt.alias)
308
+		if got != tt.want {
309
+			t.Errorf("host(%q).Matches(%q): got %v, want %v", tt.in, tt.alias, got, tt.want)
310
+		}
311
+	}
312
+}
313
+
314
+func TestMatchUnsupported(t *testing.T) {
315
+	us := &UserSettings{
316
+		userConfigFinder: testConfigFinder("testdata/match-directive"),
317
+	}
318
+
319
+	_, err := us.GetStrict("test.test", "Port")
320
+	if err == nil {
321
+		t.Fatal("expected Match directive to error, didn't")
322
+	}
323
+	if !strings.Contains(err.Error(), "ssh_config: Match directive parsing is unsupported") {
324
+		t.Errorf("wrong error: %v", err)
325
+	}
326
+}
327
+
328
+func TestIndexInRange(t *testing.T) {
329
+	us := &UserSettings{
330
+		userConfigFinder: testConfigFinder("testdata/config4"),
331
+	}
332
+
333
+	user, err := us.GetStrict("wap", "User")
334
+	if err != nil {
335
+		t.Fatal(err)
336
+	}
337
+	if user != "root" {
338
+		t.Errorf("expected User to be %q, got %q", "root", user)
339
+	}
340
+}

+ 48
- 0
vendor/src/github.com/kevinburke/ssh_config/example_test.go View File

@@ -0,0 +1,48 @@
1
+package ssh_config_test
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+
7
+	"github.com/kevinburke/ssh_config"
8
+)
9
+
10
+func ExampleHost_Matches() {
11
+	pat, _ := ssh_config.NewPattern("test.*.example.com")
12
+	host := &ssh_config.Host{Patterns: []*ssh_config.Pattern{pat}}
13
+	fmt.Println(host.Matches("test.stage.example.com"))
14
+	fmt.Println(host.Matches("othersubdomain.example.com"))
15
+	// Output:
16
+	// true
17
+	// false
18
+}
19
+
20
+func ExamplePattern() {
21
+	pat, _ := ssh_config.NewPattern("*")
22
+	host := &ssh_config.Host{Patterns: []*ssh_config.Pattern{pat}}
23
+	fmt.Println(host.Matches("test.stage.example.com"))
24
+	fmt.Println(host.Matches("othersubdomain.any.any"))
25
+	// Output:
26
+	// true
27
+	// true
28
+}
29
+
30
+func ExampleDecode() {
31
+	var config = `
32
+Host *.example.com
33
+  Compression yes
34
+`
35
+
36
+	cfg, _ := ssh_config.Decode(strings.NewReader(config))
37
+	val, _ := cfg.Get("test.example.com", "Compression")
38
+	fmt.Println(val)
39
+	// Output: yes
40
+}
41
+
42
+func ExampleDefault() {
43
+	fmt.Println(ssh_config.Default("Port"))
44
+	fmt.Println(ssh_config.Default("UnknownVar"))
45
+	// Output:
46
+	// 22
47
+	//
48
+}

+ 235
- 0
vendor/src/github.com/kevinburke/ssh_config/lexer.go View File

@@ -0,0 +1,235 @@
1
+package ssh_config
2
+
3
+import (
4
+	"io"
5
+
6
+	buffruneio "github.com/pelletier/go-buffruneio"
7
+)
8
+
9
+// Define state functions
10
+type sshLexStateFn func() sshLexStateFn
11
+
12
+type sshLexer struct {
13
+	input         *buffruneio.Reader // Textual source
14
+	buffer        []rune             // Runes composing the current token
15
+	tokens        chan token
16
+	line          uint32
17
+	col           uint16
18
+	endbufferLine uint32
19
+	endbufferCol  uint16
20
+}
21
+
22
+func (s *sshLexer) lexComment(previousState sshLexStateFn) sshLexStateFn {
23
+	return func() sshLexStateFn {
24
+		growingString := ""
25
+		for next := s.peek(); next != '\n' && next != eof; next = s.peek() {
26
+			if next == '\r' && s.follow("\r\n") {
27
+				break
28
+			}
29
+			growingString += string(next)
30
+			s.next()
31
+		}
32
+		s.emitWithValue(tokenComment, growingString)
33
+		s.skip()
34
+		return previousState
35
+	}
36
+}
37
+
38
+// lex the space after an equals sign in a function
39
+func (s *sshLexer) lexRspace() sshLexStateFn {
40
+	for {
41
+		next := s.peek()
42
+		if !isSpace(next) {
43
+			break
44
+		}
45
+		s.skip()
46
+	}
47
+	return s.lexRvalue
48
+}
49
+
50
+func (s *sshLexer) lexEquals() sshLexStateFn {
51
+	for {
52
+		next := s.peek()
53
+		if next == '=' {
54
+			s.emit(tokenEquals)
55
+			s.skip()
56
+			return s.lexRspace
57
+		}
58
+		// TODO error handling here; newline eof etc.
59
+		if !isSpace(next) {
60
+			break
61
+		}
62
+		s.skip()
63
+	}
64
+	return s.lexRvalue
65
+}
66
+
67
+func (s *sshLexer) lexKey() sshLexStateFn {
68
+	growingString := ""
69
+
70
+	for r := s.peek(); isKeyChar(r); r = s.peek() {
71
+		// simplified a lot here
72
+		if isSpace(r) || r == '=' {
73
+			s.emitWithValue(tokenKey, growingString)
74
+			s.skip()
75
+			return s.lexEquals
76
+		}
77
+		growingString += string(r)
78
+		s.next()
79
+	}
80
+	s.emitWithValue(tokenKey, growingString)
81
+	return s.lexEquals
82
+}
83
+
84
+func (s *sshLexer) lexRvalue() sshLexStateFn {
85
+	growingString := ""
86
+	for {
87
+		next := s.peek()
88
+		switch next {
89
+		case '\n':
90
+			s.emitWithValue(tokenString, growingString)
91
+			s.skip()
92
+			return s.lexVoid
93
+		case '#':
94
+			s.emitWithValue(tokenString, growingString)
95
+			s.skip()
96
+			return s.lexComment(s.lexVoid)
97
+		case eof:
98
+			s.next()
99
+		}
100
+		if next == eof {
101
+			break
102
+		}
103
+		growingString += string(next)
104
+		s.next()
105
+	}
106
+	s.emit(tokenEOF)
107
+	return nil
108
+}
109
+
110
+func (s *sshLexer) read() rune {
111
+	r, _, err := s.input.ReadRune()
112
+	if err != nil {
113
+		panic(err)
114
+	}
115
+	if r == '\n' {
116
+		s.endbufferLine++
117
+		s.endbufferCol = 1
118
+	} else {
119
+		s.endbufferCol++
120
+	}
121
+	return r
122
+}
123
+
124
+func (s *sshLexer) next() rune {
125
+	r := s.read()
126
+
127
+	if r != eof {
128
+		s.buffer = append(s.buffer, r)
129
+	}
130
+	return r
131
+}
132
+
133
+func (s *sshLexer) lexVoid() sshLexStateFn {
134
+	for {
135
+		next := s.peek()
136
+		switch next {
137
+		case '#':
138
+			s.skip()
139
+			return s.lexComment(s.lexVoid)
140
+		case '\r':
141
+			fallthrough
142
+		case '\n':
143
+			s.emit(tokenEmptyLine)
144
+			s.skip()
145
+			continue
146
+		}
147
+
148
+		if isSpace(next) {
149
+			s.skip()
150
+		}
151
+
152
+		if isKeyStartChar(next) {
153
+			return s.lexKey
154
+		}
155
+
156
+		// removed IsKeyStartChar and lexKey. probably will need to readd
157
+
158
+		if next == eof {
159
+			s.next()
160
+			break
161
+		}
162
+	}
163
+
164
+	s.emit(tokenEOF)
165
+	return nil
166
+}
167
+
168
+func (s *sshLexer) ignore() {
169
+	s.buffer = make([]rune, 0)
170
+	s.line = s.endbufferLine
171
+	s.col = s.endbufferCol
172
+}
173
+
174
+func (s *sshLexer) skip() {
175
+	s.next()
176
+	s.ignore()
177
+}
178
+
179
+func (s *sshLexer) emit(t tokenType) {
180
+	s.emitWithValue(t, string(s.buffer))
181
+}
182
+
183
+func (s *sshLexer) emitWithValue(t tokenType, value string) {
184
+	tok := token{
185
+		Position: Position{s.line, s.col},
186
+		typ:      t,
187
+		val:      value,
188
+	}
189
+	s.tokens <- tok
190
+	s.ignore()
191
+}
192
+
193
+func (s *sshLexer) peek() rune {
194
+	r, _, err := s.input.ReadRune()
195
+	if err != nil {
196
+		panic(err)
197
+	}
198
+	s.input.UnreadRune()
199
+	return r
200
+}
201
+
202
+func (s *sshLexer) follow(next string) bool {
203
+	for _, expectedRune := range next {
204
+		r, _, err := s.input.ReadRune()
205
+		defer s.input.UnreadRune()
206
+		if err != nil {
207
+			panic(err)
208
+		}
209
+		if expectedRune != r {
210
+			return false
211
+		}
212
+	}
213
+	return true
214
+}
215
+
216
+func (s *sshLexer) run() {
217
+	for state := s.lexVoid; state != nil; {
218
+		state = state()
219
+	}
220
+	close(s.tokens)
221
+}
222
+
223
+func lexSSH(input io.Reader) chan token {
224
+	bufferedInput := buffruneio.NewReader(input)
225
+	l := &sshLexer{
226
+		input:         bufferedInput,
227
+		tokens:        make(chan token),
228
+		line:          1,
229
+		col:           1,
230
+		endbufferLine: 1,
231
+		endbufferCol:  1,
232
+	}
233
+	go l.run()
234
+	return l.tokens
235
+}

+ 182
- 0
vendor/src/github.com/kevinburke/ssh_config/parser.go View File

@@ -0,0 +1,182 @@
1
+package ssh_config
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+)
7
+
8
+type sshParser struct {
9
+	flow          chan token
10
+	config        *Config
11
+	tokensBuffer  []token
12
+	currentTable  []string
13
+	seenTableKeys []string
14
+	// /etc/ssh parser or local parser - used to find the default for relative
15
+	// filepaths in the Include directive
16
+	system bool
17
+	depth  uint8
18
+}
19
+
20
+type sshParserStateFn func() sshParserStateFn
21
+
22
+// Formats and panics an error message based on a token
23
+func (p *sshParser) raiseErrorf(tok *token, msg string, args ...interface{}) {
24
+	// TODO this format is ugly
25
+	panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
26
+}
27
+
28
+func (p *sshParser) raiseError(tok *token, err error) {
29
+	if err == ErrDepthExceeded {
30
+		panic(err)
31
+	}
32
+	// TODO this format is ugly
33
+	panic(tok.Position.String() + ": " + err.Error())
34
+}
35
+
36
+func (p *sshParser) run() {
37
+	for state := p.parseStart; state != nil; {
38
+		state = state()
39
+	}
40
+}
41
+
42
+func (p *sshParser) peek() *token {
43
+	if len(p.tokensBuffer) != 0 {
44
+		return &(p.tokensBuffer[0])
45
+	}
46
+
47
+	tok, ok := <-p.flow
48
+	if !ok {
49
+		return nil
50
+	}
51
+	p.tokensBuffer = append(p.tokensBuffer, tok)
52
+	return &tok
53
+}
54
+
55
+func (p *sshParser) getToken() *token {
56
+	if len(p.tokensBuffer) != 0 {
57
+		tok := p.tokensBuffer[0]
58
+		p.tokensBuffer = p.tokensBuffer[1:]
59
+		return &tok
60
+	}
61
+	tok, ok := <-p.flow
62
+	if !ok {
63
+		return nil
64
+	}
65
+	return &tok
66
+}
67
+
68
+func (p *sshParser) parseStart() sshParserStateFn {
69
+	tok := p.peek()
70
+
71
+	// end of stream, parsing is finished
72
+	if tok == nil {
73
+		return nil
74
+	}
75
+
76
+	switch tok.typ {
77
+	case tokenComment, tokenEmptyLine:
78
+		return p.parseComment
79
+	case tokenKey:
80
+		return p.parseKV
81
+	case tokenEOF:
82
+		return nil
83
+	default:
84
+		p.raiseErrorf(tok, fmt.Sprintf("unexpected token %q\n", tok))
85
+	}
86
+	return nil
87
+}
88
+
89
+func (p *sshParser) parseKV() sshParserStateFn {
90
+	key := p.getToken()
91
+	hasEquals := false
92
+	val := p.getToken()
93
+	if val.typ == tokenEquals {
94
+		hasEquals = true
95
+		val = p.getToken()
96
+	}
97
+	comment := ""
98
+	tok := p.peek()
99
+	if tok.typ == tokenComment && tok.Position.Line == val.Position.Line {
100
+		tok = p.getToken()
101
+		comment = tok.val
102
+	}
103
+	if strings.ToLower(key.val) == "match" {
104
+		// https://github.com/kevinburke/ssh_config/issues/6
105
+		p.raiseErrorf(val, "ssh_config: Match directive parsing is unsupported")
106
+		return nil
107
+	}
108
+	if strings.ToLower(key.val) == "host" {
109
+		strPatterns := strings.Split(val.val, " ")
110
+		patterns := make([]*Pattern, 0)
111
+		for i := range strPatterns {
112
+			if strPatterns[i] == "" {
113
+				continue
114
+			}
115
+			pat, err := NewPattern(strPatterns[i])
116
+			if err != nil {
117
+				p.raiseErrorf(val, "Invalid host pattern: %v", err)
118
+				return nil
119
+			}
120
+			patterns = append(patterns, pat)
121
+		}
122
+		p.config.Hosts = append(p.config.Hosts, &Host{
123
+			Patterns:   patterns,
124
+			Nodes:      make([]Node, 0),
125
+			EOLComment: comment,
126
+			hasEquals:  hasEquals,
127
+		})
128
+		return p.parseStart
129
+	}
130
+	lastHost := p.config.Hosts[len(p.config.Hosts)-1]
131
+	if strings.ToLower(key.val) == "include" {
132
+		inc, err := NewInclude(strings.Split(val.val, " "), hasEquals, key.Position, comment, p.system, p.depth+1)
133
+		if err == ErrDepthExceeded {
134
+			p.raiseError(val, err)
135
+			return nil
136
+		}
137
+		if err != nil {
138
+			p.raiseErrorf(val, "Error parsing Include directive: %v", err)
139
+			return nil
140
+		}
141
+		lastHost.Nodes = append(lastHost.Nodes, inc)
142
+		return p.parseStart
143
+	}
144
+	kv := &KV{
145
+		Key:          key.val,
146
+		Value:        val.val,
147
+		Comment:      comment,
148
+		hasEquals:    hasEquals,
149
+		leadingSpace: uint16(key.Position.Col) - 1,
150
+		position:     key.Position,
151
+	}
152
+	lastHost.Nodes = append(lastHost.Nodes, kv)
153
+	return p.parseStart
154
+}
155
+
156
+func (p *sshParser) parseComment() sshParserStateFn {
157
+	comment := p.getToken()
158
+	lastHost := p.config.Hosts[len(p.config.Hosts)-1]
159
+	lastHost.Nodes = append(lastHost.Nodes, &Empty{
160
+		Comment: comment.val,
161
+		// account for the "#" as well
162
+		leadingSpace: comment.Position.Col - 2,
163
+		position:     comment.Position,
164
+	})
165
+	return p.parseStart
166
+}
167
+
168
+func parseSSH(flow chan token, system bool, depth uint8) *Config {
169
+	result := newConfig()
170
+	result.position = Position{1, 1}
171
+	parser := &sshParser{
172
+		flow:          flow,
173
+		config:        result,
174
+		tokensBuffer:  make([]token, 0),
175
+		currentTable:  make([]string, 0),
176
+		seenTableKeys: make([]string, 0),
177
+		system:        system,
178
+		depth:         depth,
179
+	}
180
+	parser.run()
181
+	return result
182
+}

+ 25
- 0
vendor/src/github.com/kevinburke/ssh_config/position.go View File

@@ -0,0 +1,25 @@
1
+package ssh_config
2
+
3
+import "fmt"
4
+
5
+// Position of a document element within a SSH document.
6
+//
7
+// Line and Col are both 1-indexed positions for the element's line number and
8
+// column number, respectively.  Values of zero or less will cause Invalid(),
9
+// to return true.
10
+type Position struct {
11
+	Line uint32 // line within the document
12
+	Col  uint16 // column within the line
13
+}
14
+
15
+// String representation of the position.
16
+// Displays 1-indexed line and column numbers.
17
+func (p Position) String() string {
18
+	return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
19
+}
20
+
21
+// Invalid returns whether or not the position is valid (i.e. with negative or
22
+// null values)
23
+func (p Position) Invalid() bool {
24
+	return p.Line <= 0 || p.Col <= 0
25
+}

+ 3
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/anotherfile View File

@@ -0,0 +1,3 @@
1
+# Not sure that this actually works; Include might need to be relative to the
2
+# load directory.
3
+Compression yes

+ 39
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/config1 View File

@@ -0,0 +1,39 @@
1
+Host localhost 127.0.0.1 # A comment at the end of a host line.
2
+  NoHostAuthenticationForLocalhost yes
3
+
4
+# A comment
5
+    # A comment with leading spaces.
6
+
7
+Host wap
8
+  User root
9
+  KexAlgorithms diffie-hellman-group1-sha1
10
+
11
+Host [some stuff behind a NAT]
12
+  Compression yes
13
+  ProxyCommand ssh -qW %h:%p [NATrouter]
14
+
15
+Host wopr # there are 2 proxies available for this one...
16
+  User root
17
+  ProxyCommand sh -c "ssh proxy1 -qW %h:22 || ssh proxy2 -qW %h:22"
18
+
19
+Host dhcp-??
20
+  UserKnownHostsFile /dev/null
21
+  StrictHostKeyChecking no
22
+  User root
23
+
24
+Host [my boxes] [*.mydomain]
25
+  ForwardAgent yes
26
+  ForwardX11 yes
27
+  ForwardX11Trusted yes
28
+
29
+Host *
30
+  #ControlMaster auto
31
+  #ControlPath /tmp/ssh-master-%C
32
+  #ControlPath /tmp/ssh-%u-%r@%h:%p
33
+  #ControlPersist yes
34
+  ForwardX11Timeout 52w
35
+  XAuthLocation /usr/bin/xauth
36
+  SendEnv LANG LC_*
37
+  HostKeyAlgorithms ssh-ed25519,ssh-rsa
38
+  AddressFamily inet
39
+  #UpdateHostKeys ask

+ 50
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/config2 View File

@@ -0,0 +1,50 @@
1
+#	$OpenBSD: ssh_config,v 1.30 2016/02/20 23:06:23 sobrado Exp $
2
+
3
+# This is the ssh client system-wide configuration file.  See
4
+# ssh_config(5) for more information.  This file provides defaults for
5
+# users, and the values can be changed in per-user configuration files
6
+# or on the command line.
7
+
8
+# Configuration data is parsed as follows:
9
+#  1. command line options
10
+#  2. user-specific file
11
+#  3. system-wide file
12
+# Any configuration value is only changed the first time it is set.
13
+# Thus, host-specific definitions should be at the beginning of the
14
+# configuration file, and defaults at the end.
15
+
16
+# Site-wide defaults for some commonly used options.  For a comprehensive
17
+# list of available options, their meanings and defaults, please see the
18
+# ssh_config(5) man page.
19
+
20
+# Host *
21
+#   ForwardAgent no
22
+#   ForwardX11 no
23
+#   RhostsRSAAuthentication no
24
+#   RSAAuthentication yes
25
+#   PasswordAuthentication yes
26
+#   HostbasedAuthentication no
27
+#   GSSAPIAuthentication no
28
+#   GSSAPIDelegateCredentials no
29
+#   BatchMode no
30
+#   CheckHostIP yes
31
+#   AddressFamily any
32
+#   ConnectTimeout 0
33
+#   StrictHostKeyChecking ask
34
+#   IdentityFile ~/.ssh/identity
35
+#   IdentityFile ~/.ssh/id_rsa
36
+#   IdentityFile ~/.ssh/id_dsa
37
+#   IdentityFile ~/.ssh/id_ecdsa
38
+#   IdentityFile ~/.ssh/id_ed25519
39
+#   Port 22
40
+#   Protocol 2
41
+#   Cipher 3des
42
+#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc
43
+#   MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160
44
+#   EscapeChar ~
45
+#   Tunnel no
46
+#   TunnelDevice any:any
47
+#   PermitLocalCommand no
48
+#   VisualHostKey no
49
+#   ProxyCommand ssh -q -W %h:%p gateway.example.com
50
+#   RekeyLimit 1G 1h

+ 31
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/config3 View File

@@ -0,0 +1,31 @@
1
+Host bastion.*.i.*.example.net
2
+  User simon.thulbourn
3
+  Port 22
4
+  ForwardAgent yes
5
+  IdentityFile /Users/%u/.ssh/example.net/%r/id_rsa
6
+  UseKeychain yes
7
+
8
+Host 10.*
9
+  User simon.thulbourn
10
+  Port 23
11
+  ForwardAgent yes
12
+  StrictHostKeyChecking no
13
+  UserKnownHostsFile /dev/null
14
+  IdentityFile /Users/%u/.ssh/example.net/%r/id_rsa
15
+  UseKeychain yes
16
+  ProxyCommand >&1; h="%h"; exec ssh -q $(ssh-bastion -ip $h) nc %h %p
17
+
18
+Host 20.20.20.?
19
+  User simon.thulbourn
20
+  Port 24
21
+  ForwardAgent yes
22
+  StrictHostKeyChecking no
23
+  UserKnownHostsFile /dev/null
24
+  IdentityFile /Users/%u/.ssh/example.net/%r/id_rsa
25
+  UseKeychain yes
26
+  ProxyCommand >&1; h="%h"; exec ssh -q $(ssh-bastion -ip $h) nc %h %p
27
+
28
+Host *
29
+  IdentityFile /Users/%u/.ssh/%h/%r/id_rsa
30
+  UseKeychain yes
31
+  Port 25

+ 4
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/config4 View File

@@ -0,0 +1,4 @@
1
+# Extra space at end of line is important.
2
+Host wap     
3
+  User root
4
+  KexAlgorithms diffie-hellman-group1-sha1

+ 4
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/eqsign View File

@@ -0,0 +1,4 @@
1
+Host=test.test
2
+  Port =1234
3
+  Port2= 5678
4
+  Compression yes

+ 2
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/extraspace View File

@@ -0,0 +1,2 @@
1
+Host test.test
2
+  Port      1234

+ 4
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/include View File

@@ -0,0 +1,4 @@
1
+Host kevinburke.ssh_config.test.example.com
2
+    # This file (or files) needs to be found in ~/.ssh or /etc/ssh, depending on
3
+    # the test.
4
+    Include kevinburke-ssh-config-*-file

+ 4
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/include-recursive View File

@@ -0,0 +1,4 @@
1
+Host kevinburke.ssh_config.test.example.com
2
+	# This file (or files) needs to be found in ~/.ssh or /etc/ssh, depending on
3
+	# the test. It should include itself.
4
+	Include kevinburke-ssh-config-recursive-include

+ 2
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/invalid-port View File

@@ -0,0 +1,2 @@
1
+Host test.test
2
+  Port notanumber

+ 2
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/match-directive View File

@@ -0,0 +1,2 @@
1
+Match all
2
+	Port 4567

+ 5
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/negated View File

@@ -0,0 +1,5 @@
1
+Host *.example.com !*.dialup.example.com
2
+  Port 1234
3
+
4
+Host *
5
+  Port 5678

+ 0
- 0
vendor/src/github.com/kevinburke/ssh_config/testdata/system-include View File


+ 49
- 0
vendor/src/github.com/kevinburke/ssh_config/token.go View File

@@ -0,0 +1,49 @@
1
+package ssh_config
2
+
3
+import "fmt"
4
+
5
+type token struct {
6
+	Position
7
+	typ tokenType
8
+	val string
9
+}
10
+
11
+func (t token) String() string {
12
+	switch t.typ {
13
+	case tokenEOF:
14
+		return "EOF"
15
+	}
16
+	return fmt.Sprintf("%q", t.val)
17
+}
18
+
19
+type tokenType int
20
+
21
+const (
22
+	eof = -(iota + 1)
23
+)
24
+
25
+const (
26
+	tokenError tokenType = iota
27
+	tokenEOF
28
+	tokenEmptyLine
29
+	tokenComment
30
+	tokenKey
31
+	tokenEquals
32
+	tokenString
33
+)
34
+
35
+func isSpace(r rune) bool {
36
+	return r == ' ' || r == '\t'
37
+}
38
+
39
+func isKeyStartChar(r rune) bool {
40
+	return !(isSpace(r) || r == '\r' || r == '\n' || r == eof)
41
+}
42
+
43
+// I'm not sure that this is correct
44
+func isKeyChar(r rune) bool {
45
+	// Keys start with the first character that isn't whitespace or [ and end
46
+	// with the last non-whitespace character before the equals sign. Keys
47
+	// cannot contain a # character."
48
+	return !(r == '\r' || r == '\n' || r == eof || r == '=')
49
+}

+ 162
- 0
vendor/src/github.com/kevinburke/ssh_config/validators.go View File

@@ -0,0 +1,162 @@
1
+package ssh_config
2
+
3
+import (
4
+	"fmt"
5
+	"strconv"
6
+	"strings"
7
+)
8
+
9
+// Default returns the default value for the given keyword, for example "22" if
10
+// the keyword is "Port". Default returns the empty string if the keyword has no
11
+// default, or if the keyword is unknown. Keyword matching is case-insensitive.
12
+//
13
+// Default values are provided by OpenSSH_7.4p1 on a Mac.
14
+func Default(keyword string) string {
15
+	return defaults[strings.ToLower(keyword)]
16
+}
17
+
18
+// Arguments where the value must be "yes" or "no" and *only* yes or no.
19
+var yesnos = map[string]bool{
20
+	strings.ToLower("BatchMode"):                        true,
21
+	strings.ToLower("CanonicalizeFallbackLocal"):        true,
22
+	strings.ToLower("ChallengeResponseAuthentication"):  true,
23
+	strings.ToLower("CheckHostIP"):                      true,
24
+	strings.ToLower("ClearAllForwardings"):              true,
25
+	strings.ToLower("Compression"):                      true,
26
+	strings.ToLower("EnableSSHKeysign"):                 true,
27
+	strings.ToLower("ExitOnForwardFailure"):             true,
28
+	strings.ToLower("ForwardAgent"):                     true,
29
+	strings.ToLower("ForwardX11"):                       true,
30
+	strings.ToLower("ForwardX11Trusted"):                true,
31
+	strings.ToLower("GatewayPorts"):                     true,
32
+	strings.ToLower("GSSAPIAuthentication"):             true,
33
+	strings.ToLower("GSSAPIDelegateCredentials"):        true,
34
+	strings.ToLower("HostbasedAuthentication"):          true,
35
+	strings.ToLower("IdentitiesOnly"):                   true,
36
+	strings.ToLower("KbdInteractiveAuthentication"):     true,
37
+	strings.ToLower("NoHostAuthenticationForLocalhost"): true,
38
+	strings.ToLower("PasswordAuthentication"):           true,
39
+	strings.ToLower("PermitLocalCommand"):               true,
40
+	strings.ToLower("PubkeyAuthentication"):             true,
41
+	strings.ToLower("RhostsRSAAuthentication"):          true,
42
+	strings.ToLower("RSAAuthentication"):                true,
43
+	strings.ToLower("StreamLocalBindUnlink"):            true,
44
+	strings.ToLower("TCPKeepAlive"):                     true,
45
+	strings.ToLower("UseKeychain"):                      true,
46
+	strings.ToLower("UsePrivilegedPort"):                true,
47
+	strings.ToLower("VisualHostKey"):                    true,
48
+}
49
+
50
+var uints = map[string]bool{
51
+	strings.ToLower("CanonicalizeMaxDots"):     true,
52
+	strings.ToLower("CompressionLevel"):        true, // 1 to 9
53
+	strings.ToLower("ConnectionAttempts"):      true,
54
+	strings.ToLower("ConnectTimeout"):          true,
55
+	strings.ToLower("NumberOfPasswordPrompts"): true,
56
+	strings.ToLower("Port"):                    true,
57
+	strings.ToLower("ServerAliveCountMax"):     true,
58
+	strings.ToLower("ServerAliveInterval"):     true,
59
+}
60
+
61
+func mustBeYesOrNo(lkey string) bool {
62
+	return yesnos[lkey]
63
+}
64
+
65
+func mustBeUint(lkey string) bool {
66
+	return uints[lkey]
67
+}
68
+
69
+func validate(key, val string) error {
70
+	lkey := strings.ToLower(key)
71
+	if mustBeYesOrNo(lkey) && (val != "yes" && val != "no") {
72
+		return fmt.Errorf("ssh_config: value for key %q must be 'yes' or 'no', got %q", key, val)
73
+	}
74
+	if mustBeUint(lkey) {
75
+		_, err := strconv.ParseUint(val, 10, 64)
76
+		if err != nil {
77
+			return fmt.Errorf("ssh_config: %v", err)
78
+		}
79
+	}
80
+	return nil
81
+}
82
+
83
+var defaults = map[string]string{
84
+	strings.ToLower("AddKeysToAgent"):                  "no",
85
+	strings.ToLower("AddressFamily"):                   "any",
86
+	strings.ToLower("BatchMode"):                       "no",
87
+	strings.ToLower("CanonicalizeFallbackLocal"):       "yes",
88
+	strings.ToLower("CanonicalizeHostname"):            "no",
89
+	strings.ToLower("CanonicalizeMaxDots"):             "1",
90
+	strings.ToLower("ChallengeResponseAuthentication"): "yes",
91
+	strings.ToLower("CheckHostIP"):                     "yes",
92
+	// TODO is this still the correct cipher
93
+	strings.ToLower("Cipher"):                    "3des",
94
+	strings.ToLower("Ciphers"):                   "chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc",
95
+	strings.ToLower("ClearAllForwardings"):       "no",
96
+	strings.ToLower("Compression"):               "no",
97
+	strings.ToLower("CompressionLevel"):          "6",
98
+	strings.ToLower("ConnectionAttempts"):        "1",
99
+	strings.ToLower("ControlMaster"):             "no",
100
+	strings.ToLower("EnableSSHKeysign"):          "no",
101
+	strings.ToLower("EscapeChar"):                "~",
102
+	strings.ToLower("ExitOnForwardFailure"):      "no",
103
+	strings.ToLower("FingerprintHash"):           "sha256",
104
+	strings.ToLower("ForwardAgent"):              "no",
105
+	strings.ToLower("ForwardX11"):                "no",
106
+	strings.ToLower("ForwardX11Timeout"):         "20m",
107
+	strings.ToLower("ForwardX11Trusted"):         "no",
108
+	strings.ToLower("GatewayPorts"):              "no",
109
+	strings.ToLower("GlobalKnownHostsFile"):      "/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2",
110
+	strings.ToLower("GSSAPIAuthentication"):      "no",
111
+	strings.ToLower("GSSAPIDelegateCredentials"): "no",
112
+	strings.ToLower("HashKnownHosts"):            "no",
113
+	strings.ToLower("HostbasedAuthentication"):   "no",
114
+
115
+	strings.ToLower("HostbasedKeyTypes"): "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
116
+	strings.ToLower("HostKeyAlgorithms"): "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
117
+	// HostName has a dynamic default (the value passed at the command line).
118
+
119
+	strings.ToLower("IdentitiesOnly"): "no",
120
+	strings.ToLower("IdentityFile"):   "~/.ssh/identity",
121
+
122
+	// IPQoS has a dynamic default based on interactive or non-interactive
123
+	// sessions.
124
+
125
+	strings.ToLower("KbdInteractiveAuthentication"): "yes",
126
+
127
+	strings.ToLower("KexAlgorithms"): "curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1",
128
+	strings.ToLower("LogLevel"):      "INFO",
129
+	strings.ToLower("MACs"):          "umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1",
130
+
131
+	strings.ToLower("NoHostAuthenticationForLocalhost"): "no",
132
+	strings.ToLower("NumberOfPasswordPrompts"):          "3",
133
+	strings.ToLower("PasswordAuthentication"):           "yes",
134
+	strings.ToLower("PermitLocalCommand"):               "no",
135
+	strings.ToLower("Port"):                             "22",
136
+
137
+	strings.ToLower("PreferredAuthentications"): "gssapi-with-mic,hostbased,publickey,keyboard-interactive,password",
138
+	strings.ToLower("Protocol"):                 "2",
139
+	strings.ToLower("ProxyUseFdpass"):           "no",
140
+	strings.ToLower("PubkeyAcceptedKeyTypes"):   "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
141
+	strings.ToLower("PubkeyAuthentication"):     "yes",
142
+	strings.ToLower("RekeyLimit"):               "default none",
143
+	strings.ToLower("RhostsRSAAuthentication"):  "no",
144
+	strings.ToLower("RSAAuthentication"):        "yes",
145
+
146
+	strings.ToLower("ServerAliveCountMax"):   "3",
147
+	strings.ToLower("ServerAliveInterval"):   "0",
148
+	strings.ToLower("StreamLocalBindMask"):   "0177",
149
+	strings.ToLower("StreamLocalBindUnlink"): "no",
150
+	strings.ToLower("StrictHostKeyChecking"): "ask",
151
+	strings.ToLower("TCPKeepAlive"):          "yes",
152
+	strings.ToLower("Tunnel"):                "no",
153
+	strings.ToLower("TunnelDevice"):          "any:any",
154
+	strings.ToLower("UpdateHostKeys"):        "no",
155
+	strings.ToLower("UseKeychain"):           "no",
156
+	strings.ToLower("UsePrivilegedPort"):     "no",
157
+
158
+	strings.ToLower("UserKnownHostsFile"): "~/.ssh/known_hosts ~/.ssh/known_hosts2",
159
+	strings.ToLower("VerifyHostKeyDNS"):   "no",
160
+	strings.ToLower("VisualHostKey"):      "no",
161
+	strings.ToLower("XAuthLocation"):      "/usr/X11R6/bin/xauth",
162
+}

+ 44
- 0
vendor/src/github.com/kevinburke/ssh_config/validators_test.go View File

@@ -0,0 +1,44 @@
1
+package ssh_config
2
+
3
+import (
4
+	"testing"
5
+)
6
+
7
+var validateTests = []struct {
8
+	key string
9
+	val string
10
+	err string
11
+}{
12
+	{"IdentitiesOnly", "yes", ""},
13
+	{"IdentitiesOnly", "Yes", `ssh_config: value for key "IdentitiesOnly" must be 'yes' or 'no', got "Yes"`},
14
+	{"Port", "22", ``},
15
+	{"Port", "yes", `ssh_config: strconv.ParseUint: parsing "yes": invalid syntax`},
16
+}
17
+
18
+func TestValidate(t *testing.T) {
19
+	for _, tt := range validateTests {
20
+		err := validate(tt.key, tt.val)
21
+		if tt.err == "" && err != nil {
22
+			t.Errorf("validate(%q, %q): got %v, want nil", tt.key, tt.val, err)
23
+		}
24
+		if tt.err != "" {
25
+			if err == nil {
26
+				t.Errorf("validate(%q, %q): got nil error, want %v", tt.key, tt.val, tt.err)
27
+			} else if err.Error() != tt.err {
28
+				t.Errorf("validate(%q, %q): got err %v, want %v", tt.key, tt.val, err, tt.err)
29
+			}
30
+		}
31
+	}
32
+}
33
+
34
+func TestDefault(t *testing.T) {
35
+	if v := Default("VisualHostKey"); v != "no" {
36
+		t.Errorf("Default(%q): got %v, want 'no'", "VisualHostKey", v)
37
+	}
38
+	if v := Default("visualhostkey"); v != "no" {
39
+		t.Errorf("Default(%q): got %v, want 'no'", "visualhostkey", v)
40
+	}
41
+	if v := Default("notfound"); v != "" {
42
+		t.Errorf("Default(%q): got %v, want ''", "notfound", v)
43
+	}
44
+}

+ 61
- 0
vendor/src/github.com/pelletier/go-buffruneio/README.md View File

@@ -0,0 +1,61 @@
1
+# buffruneio
2
+
3
+[![Tests Status](https://travis-ci.org/pelletier/go-buffruneio.svg?branch=master)](https://travis-ci.org/pelletier/go-buffruneio)
4
+[![GoDoc](https://godoc.org/github.com/pelletier/go-buffruneio?status.svg)](https://godoc.org/github.com/pelletier/go-buffruneio)
5
+
6
+Buffruneio provides rune-based buffered input.
7
+
8
+```go
9
+import "github.com/pelletier/go-buffruneio"
10
+```
11
+
12
+## Examples
13
+
14
+```go
15
+import (
16
+    "fmt"
17
+    "github.com/pelletier/go-buffruneio"
18
+    "strings"
19
+)
20
+
21
+reader := buffruneio.NewReader(strings.NewReader("abcd"))
22
+fmt.Println(reader.ReadRune()) // 'a'
23
+fmt.Println(reader.ReadRune()) // 'b'
24
+fmt.Println(reader.ReadRune()) // 'c'
25
+reader.UnreadRune()
26
+reader.UnreadRune()
27
+fmt.Println(reader.ReadRune()) // 'b'
28
+fmt.Println(reader.ReadRune()) // 'c'
29
+```
30
+
31
+## Documentation
32
+
33
+The documentation and additional examples are available at
34
+[godoc.org](http://godoc.org/github.com/pelletier/go-buffruneio).
35
+
36
+## Contribute
37
+
38
+Feel free to report bugs and patches using GitHub's pull requests system on
39
+[pelletier/go-buffruneio](https://github.com/pelletier/go-buffruneio). Any feedback is
40
+much appreciated!
41
+
42
+## LICENSE
43
+
44
+Copyright (c) 2016 - 2018 Thomas Pelletier
45
+
46
+Permission is hereby granted, free of charge, to any person obtaining a copy of
47
+this software and associated documentation files (the "Software"), to deal in
48
+the Software without restriction, including without limitation the rights to
49
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
50
+the Software, and to permit persons to whom the Software is furnished to do so,
51
+subject to the following conditions:
52
+
53
+The above copyright notice and this permission notice shall be included in all
54
+copies or substantial portions of the Software.
55
+
56
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
58
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
59
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
60
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
61
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 133
- 0
vendor/src/github.com/pelletier/go-buffruneio/buffruneio.go View File

@@ -0,0 +1,133 @@
1
+// Package buffruneio provides rune-based buffered input.
2
+package buffruneio
3
+
4
+import (
5
+	"bufio"
6
+	"errors"
7
+	"io"
8
+	"unicode/utf8"
9
+)
10
+
11
+// EOF is a rune value indicating end-of-file.
12
+const EOF = -1
13
+
14
+// ErrNoRuneToUnread is the error returned when UnreadRune is called with nothing to unread.
15
+var ErrNoRuneToUnread = errors.New("no rune to unwind")
16
+
17
+// A Reader implements rune-based input for an underlying byte stream.
18
+type Reader struct {
19
+	buffer  []rune
20
+	current int
21
+	input   *bufio.Reader
22
+}
23
+
24
+// NewReader returns a new Reader reading the given input.
25
+func NewReader(input io.Reader) *Reader {
26
+	return &Reader{
27
+		input: bufio.NewReader(input),
28
+	}
29
+}
30
+
31
+// The rune buffer stores -2 to represent RuneError of length 1 (UTF-8 decoding errors).
32
+const badRune = -2
33
+
34
+// feedBuffer adds a rune to the buffer.
35
+// If EOF is reached, it adds EOF to the buffer and returns nil.
36
+// If a different error is encountered, it returns the error without
37
+// adding to the buffer.
38
+func (rd *Reader) feedBuffer() error {
39
+	if rd.buffer == nil {
40
+		rd.buffer = make([]rune, 0, 256)
41
+	}
42
+	r, size, err := rd.input.ReadRune()
43
+	if err != nil {
44
+		if err != io.EOF {
45
+			return err
46
+		}
47
+		r = EOF
48
+	}
49
+	if r == utf8.RuneError && size == 1 {
50
+		r = badRune
51
+	}
52
+	rd.buffer = append(rd.buffer, r)
53
+	return nil
54
+}
55
+
56
+// ReadRune reads and returns the next rune from the input.
57
+// The rune is also saved in an internal buffer, in case UnreadRune is called.
58
+// To avoid unbounded buffer growth, the caller must call Forget at appropriate intervals.
59
+//
60
+// At end of file, ReadRune returns EOF, 0, nil.
61
+// On read errors other than io.EOF, ReadRune returns EOF, 0, err.
62
+func (rd *Reader) ReadRune() (rune, int, error) {
63
+	if rd.current >= len(rd.buffer) {
64
+		if err := rd.feedBuffer(); err != nil {
65
+			return EOF, 0, err
66
+		}
67
+	}
68
+	r := rd.buffer[rd.current]
69
+	rd.current++
70
+	if r == badRune {
71
+		return utf8.RuneError, 1, nil
72
+	}
73
+	if r == EOF {
74
+		return EOF, 0, nil
75
+	}
76
+	return r, utf8.RuneLen(r), nil
77
+}
78
+
79
+// UnreadRune rewinds the input by one rune, undoing the effect of a single ReadRune call.
80
+// UnreadRune may be called multiple times to rewind a sequence of ReadRune calls,
81
+// up to the last time Forget was called or the beginning of the input.
82
+//
83
+// If there are no ReadRune calls left to undo, UnreadRune returns ErrNoRuneToUnread.
84
+func (rd *Reader) UnreadRune() error {
85
+	if rd.current == 0 {
86
+		return ErrNoRuneToUnread
87
+	}
88
+	rd.current--
89
+	return nil
90
+}
91
+
92
+// Forget discards buffered runes before the current input position.
93
+// Calling Forget makes it impossible to UnreadRune earlier than the current input position
94
+// but is necessary to avoid unbounded buffer growth.
95
+func (rd *Reader) Forget() {
96
+	n := copy(rd.buffer, rd.buffer[rd.current:])
97
+	rd.current = 0
98
+	rd.buffer = rd.buffer[:n]
99
+}
100
+
101
+// PeekRunes returns the next n runes in the input,
102
+// without advancing the current input position.
103
+//
104
+// If the input has fewer than n runes and then returns
105
+// an io.EOF error, PeekRune returns a slice containing
106
+// the available runes followed by EOF.
107
+// On other hand, if the input ends early with a non-io.EOF error,
108
+// PeekRune returns a slice containing only the available runes,
109
+// with no terminating EOF.
110
+func (rd *Reader) PeekRunes(n int) []rune {
111
+	for len(rd.buffer)-rd.current < n && !rd.haveEOF() {
112
+		if err := rd.feedBuffer(); err != nil {
113
+			break
114
+		}
115
+	}
116
+
117
+	res := make([]rune, 0, n)
118
+	for i := 0; i < n; i++ {
119
+		r := rd.buffer[rd.current+i]
120
+		if r == badRune {
121
+			r = utf8.RuneError
122
+		}
123
+		res = append(res, r)
124
+		if r == EOF {
125
+			break
126
+		}
127
+	}
128
+	return res
129
+}
130
+
131
+func (rd *Reader) haveEOF() bool {
132
+	return rd.current < len(rd.buffer) && rd.buffer[len(rd.buffer)-1] == EOF
133
+}

+ 264
- 0
vendor/src/github.com/pelletier/go-buffruneio/buffruneio_test.go View File

@@ -0,0 +1,264 @@
1
+package buffruneio
2
+
3
+import (
4
+	"reflect"
5
+	"runtime/debug"
6
+	"strings"
7
+	"testing"
8
+	"unicode/utf8"
9
+)
10
+
11
+func assertNoError(t *testing.T, err error) {
12
+	if err != nil {
13
+		t.Log("unexpected error", err)
14
+		debug.PrintStack()
15
+		t.FailNow()
16
+	}
17
+}
18
+
19
+func assumeRunesArray(t *testing.T, expected []rune, got []rune) {
20
+	if len(expected) != len(got) {
21
+		t.Fatal("expected", len(expected), "runes, but got", len(got))
22
+	}
23
+	for i := 0; i < len(got); i++ {
24
+		if expected[i] != got[i] {
25
+			t.Fatal("expected rune", expected[i], "at index", i, "but got", got[i])
26
+		}
27
+	}
28
+}
29
+
30
+func assumeRune(t *testing.T, rd *Reader, r rune) {
31
+	gotRune, size, err := rd.ReadRune()
32
+	wantSize := utf8.RuneLen(r)
33
+	if wantSize < 0 {
34
+		wantSize = 0
35
+	}
36
+	if gotRune != r || size != wantSize || err != nil {
37
+		t.Fatalf("ReadRune() = %q, %d, %v, wanted %q, %d, nil", gotRune, size, err, r, wantSize)
38
+	}
39
+}
40
+
41
+func assumeBadRune(t *testing.T, rd *Reader) {
42
+	gotRune, size, err := rd.ReadRune()
43
+	if gotRune != utf8.RuneError || size != 1 || err != nil {
44
+		t.Fatalf("ReadRune() = %q, %d, %v, wanted %q, 1, nil", gotRune, size, err, utf8.RuneError)
45
+	}
46
+}
47
+
48
+func TestReadString(t *testing.T) {
49
+	s := "hello"
50
+	rd := NewReader(strings.NewReader(s))
51
+
52
+	assumeRune(t, rd, 'h')
53
+	assumeRune(t, rd, 'e')
54
+	assumeRune(t, rd, 'l')
55
+	assumeRune(t, rd, 'l')
56
+	assumeRune(t, rd, 'o')
57
+	assumeRune(t, rd, EOF)
58
+}
59
+
60
+func TestMultipleEOF(t *testing.T) {
61
+	s := ""
62
+	rd := NewReader(strings.NewReader(s))
63
+
64
+	assumeRune(t, rd, EOF)
65
+	assumeRune(t, rd, EOF)
66
+}
67
+
68
+func TestBadRunes(t *testing.T) {
69
+	s := "ab\xff\ufffd\xffcd"
70
+	rd := NewReader(strings.NewReader(s))
71
+
72
+	assumeRune(t, rd, 'a')
73
+	assumeRune(t, rd, 'b')
74
+	assumeBadRune(t, rd)
75
+	assumeRune(t, rd, utf8.RuneError)
76
+	assumeBadRune(t, rd)
77
+	assumeRune(t, rd, 'c')
78
+	assumeRune(t, rd, 'd')
79
+
80
+	for i := 0; i < 6; i++ {
81
+		assertNoError(t, rd.UnreadRune())
82
+	}
83
+	assumeRune(t, rd, 'b')
84
+	assumeBadRune(t, rd)
85
+	assumeRune(t, rd, utf8.RuneError)
86
+	assumeBadRune(t, rd)
87
+	assumeRune(t, rd, 'c')
88
+	assumeRune(t, rd, 'd')
89
+}
90
+
91
+func TestUnread(t *testing.T) {
92
+	s := "ab"
93
+	rd := NewReader(strings.NewReader(s))
94
+
95
+	assumeRune(t, rd, 'a')
96
+	assumeRune(t, rd, 'b')
97
+	assertNoError(t, rd.UnreadRune())
98
+	assumeRune(t, rd, 'b')
99
+	assumeRune(t, rd, EOF)
100
+}
101
+
102
+func TestUnreadEOF(t *testing.T) {
103
+	s := "x"
104
+	rd := NewReader(strings.NewReader(s))
105
+
106
+	_ = rd.UnreadRune()
107
+	assumeRune(t, rd, 'x')
108
+	assumeRune(t, rd, EOF)
109
+	assumeRune(t, rd, EOF)
110
+	assertNoError(t, rd.UnreadRune())
111
+	assumeRune(t, rd, EOF)
112
+	assertNoError(t, rd.UnreadRune())
113
+	assertNoError(t, rd.UnreadRune())
114
+	assumeRune(t, rd, EOF)
115
+	assumeRune(t, rd, EOF)
116
+	assertNoError(t, rd.UnreadRune())
117
+	assertNoError(t, rd.UnreadRune())
118
+	assertNoError(t, rd.UnreadRune())
119
+	assumeRune(t, rd, 'x')
120
+	assumeRune(t, rd, EOF)
121
+	assumeRune(t, rd, EOF)
122
+}
123
+
124
+func TestForget(t *testing.T) {
125
+	s := "helio"
126
+	rd := NewReader(strings.NewReader(s))
127
+
128
+	assumeRune(t, rd, 'h')
129
+	assumeRune(t, rd, 'e')
130
+	assumeRune(t, rd, 'l')
131
+	assumeRune(t, rd, 'i')
132
+	rd.Forget()
133
+	if rd.UnreadRune() != ErrNoRuneToUnread {
134
+		t.Fatal("no rune should be available")
135
+	}
136
+	assumeRune(t, rd, 'o')
137
+}
138
+
139
+func TestForgetAfterUnread(t *testing.T) {
140
+	s := "helio"
141
+	rd := NewReader(strings.NewReader(s))
142
+
143
+	assumeRune(t, rd, 'h')
144
+	assumeRune(t, rd, 'e')
145
+	assumeRune(t, rd, 'l')
146
+	assumeRune(t, rd, 'i')
147
+	assertNoError(t, rd.UnreadRune())
148
+	rd.Forget()
149
+	if rd.UnreadRune() != ErrNoRuneToUnread {
150
+		t.Fatal("no rune should be available")
151
+	}
152
+	assumeRune(t, rd, 'i')
153
+	assumeRune(t, rd, 'o')
154
+}
155
+
156
+func TestForgetEmpty(t *testing.T) {
157
+	s := ""
158
+	rd := NewReader(strings.NewReader(s))
159
+
160
+	rd.Forget()
161
+	assumeRune(t, rd, EOF)
162
+	rd.Forget()
163
+}
164
+
165
+func TestPeekEmpty(t *testing.T) {
166
+	s := ""
167
+	rd := NewReader(strings.NewReader(s))
168
+
169
+	runes := rd.PeekRunes(1)
170
+	if len(runes) != 1 {
171
+		t.Fatal("incorrect number of runes", len(runes))
172
+	}
173
+	if runes[0] != EOF {
174
+		t.Fatal("incorrect rune", runes[0])
175
+	}
176
+}
177
+
178
+func TestPeek(t *testing.T) {
179
+	s := "a"
180
+	rd := NewReader(strings.NewReader(s))
181
+
182
+	runes := rd.PeekRunes(1)
183
+	assumeRunesArray(t, []rune{'a'}, runes)
184
+
185
+	runes = rd.PeekRunes(1)
186
+	assumeRunesArray(t, []rune{'a'}, runes)
187
+
188
+	assumeRune(t, rd, 'a')
189
+	runes = rd.PeekRunes(1)
190
+	assumeRunesArray(t, []rune{EOF}, runes)
191
+
192
+	assumeRune(t, rd, EOF)
193
+}
194
+
195
+func TestPeekLarge(t *testing.T) {
196
+	s := "abcdefg☺\xff☹"
197
+	rd := NewReader(strings.NewReader(s))
198
+
199
+	runes := rd.PeekRunes(100)
200
+	want := []rune{'a', 'b', 'c', 'd', 'e', 'f', 'g', '☺', utf8.RuneError, '☹', EOF}
201
+	if !reflect.DeepEqual(runes, want) {
202
+		t.Fatalf("PeekRunes(100) = %q, want %q", runes, want)
203
+	}
204
+}
205
+
206
+var bigString = strings.Repeat("abcdefghi☺\xff☹", 1024) // 16 kB
207
+
208
+const bigStringRunes = 12 * 1024 // 12k runes
209
+
210
+func BenchmarkRead16K(b *testing.B) {
211
+	// Read 16K with no unread, no forget.
212
+	benchmarkRead(b, 1, false)
213
+}
214
+
215
+func BenchmarkReadForget16K(b *testing.B) {
216
+	// Read 16K, forgetting every 128 runes.
217
+	benchmarkRead(b, 1, true)
218
+}
219
+
220
+func BenchmarkReadRewind16K(b *testing.B) {
221
+	// Read 16K, unread all, read that 16K again.
222
+	benchmarkRead(b, 2, false)
223
+}
224
+
225
+func benchmarkRead(b *testing.B, count int, forget bool) {
226
+	if len(bigString) != 16*1024 {
227
+		b.Fatal("wrong length for bigString")
228
+	}
229
+	sr0 := strings.NewReader(bigString)