Cleanup for public release

- Test Include string representation

- Add docs and examples
This commit is contained in:
Kevin Burke
2017-05-25 08:00:10 -07:00
parent 3fad3c64da
commit 7897293c53
5 changed files with 69 additions and 24 deletions

View File

@@ -297,20 +297,21 @@ func special(b byte) bool {
return bytes.IndexByte(specialBytes, b) >= 0 return bytes.IndexByte(specialBytes, b) >= 0
} }
// NewPattern creates a new Pattern for matching hosts. // NewPattern creates a new Pattern for matching hosts. NewPattern("*") creates
func NewPattern(s string) (*Pattern, error) { // a Pattern that matches all hosts.
// From the manpage: //
// A pattern consists of zero or more non-whitespace characters, // From the manpage, a pattern consists of zero or more non-whitespace
// `*' (a wildcard that matches zero or more characters), // characters, `*' (a wildcard that matches zero or more characters), or `?' (a
// or `?' (a wildcard that matches exactly one character). // wildcard that matches exactly one character). For example, to specify a set
// For example, to specify a set of declarations for any host in the // of declarations for any host in the ".co.uk" set of domains, the following
// ".co.uk" set of domains, the following pattern could be used: // pattern could be used:
// //
// Host *.co.uk // Host *.co.uk
// //
// The following pattern would match any host in the 192.168.0.[0-9] network range: // The following pattern would match any host in the 192.168.0.[0-9] network range:
// //
// Host 192.168.0.? // Host 192.168.0.?
func NewPattern(s string) (*Pattern, error) {
if s == "" { if s == "" {
return nil, errors.New("ssh_config: empty pattern") return nil, errors.New("ssh_config: empty pattern")
} }
@@ -344,6 +345,7 @@ func NewPattern(s string) (*Pattern, error) {
return &Pattern{str: s, regex: r, not: negated}, nil return &Pattern{str: s, regex: r, not: negated}, nil
} }
// Host describes a Host directive and the keywords that follow it.
type Host struct { type Host struct {
// A list of host patterns that should match this host. // A list of host patterns that should match this host.
Patterns []*Pattern Patterns []*Pattern
@@ -492,6 +494,7 @@ type Include struct {
leadingSpace uint16 leadingSpace uint16
position Position position Position
depth uint8 depth uint8
hasEquals bool
} }
const maxRecurseDepth = 5 const maxRecurseDepth = 5
@@ -519,7 +522,7 @@ func removeDups(arr []string) []string {
// Configuration files are parsed greedily (e.g. as soon as this function runs). // Configuration files are parsed greedily (e.g. as soon as this function runs).
// Any error encountered while parsing nested configuration files will be // Any error encountered while parsing nested configuration files will be
// returned. // returned.
func NewInclude(directives []string, comment string, system bool, depth uint8) (*Include, error) { func NewInclude(directives []string, hasEquals bool, pos Position, comment string, system bool, depth uint8) (*Include, error) {
if depth > maxRecurseDepth { if depth > maxRecurseDepth {
return nil, ErrDepthExceeded return nil, ErrDepthExceeded
} }
@@ -527,7 +530,10 @@ func NewInclude(directives []string, comment string, system bool, depth uint8) (
Comment: comment, Comment: comment,
directives: directives, directives: directives,
files: make(map[string]*Config), files: make(map[string]*Config),
position: pos,
leadingSpace: uint16(pos.Col) - 1,
depth: depth, depth: depth,
hasEquals: hasEquals,
} }
// no need for inc.mu.Lock() since nothing else can access this inc // no need for inc.mu.Lock() since nothing else can access this inc
matches := make([]string, 0) matches := make([]string, 0)
@@ -582,8 +588,18 @@ func (inc *Include) Get(alias, key string) string {
return "" return ""
} }
func (i *Include) String() string { // String prints out a string representation of this Include directive. Note
return "TODO" // included Config files are not printed as part of this representation.
func (inc *Include) String() string {
equals := " "
if inc.hasEquals {
equals = " = "
}
line := fmt.Sprintf("%sInclude%s%s", strings.Repeat(" ", int(inc.leadingSpace)), equals, strings.Join(inc.directives, " "))
if inc.Comment != "" {
line += " #" + inc.Comment
}
return line
} }
var matchAll *Pattern var matchAll *Pattern

View File

@@ -3,6 +3,7 @@ package ssh_config
import ( import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -253,6 +254,24 @@ func TestIncludeRecursive(t *testing.T) {
} }
} }
func TestIncludeString(t *testing.T) {
if testing.Short() {
t.Skip("skipping fs write in short mode")
}
data, err := ioutil.ReadFile("testdata/include")
if err != nil {
log.Fatal(err)
}
c, err := Decode(bytes.NewReader(data))
if err != nil {
t.Fatal(err)
}
s := c.String()
if s != string(data) {
t.Errorf("mismatch: got %q\nwant %q", s, string(data))
}
}
var matchTests = []struct { var matchTests = []struct {
in []string in []string
alias string alias string

View File

@@ -11,3 +11,13 @@ func ExampleHost_Matches() {
// true // true
// false // false
} }
func ExamplePattern() {
pat, _ := NewPattern("*")
host := &Host{Patterns: []*Pattern{pat}}
fmt.Println(host.Matches("test.stage.example.com"))
fmt.Println(host.Matches("othersubdomain.any.any"))
// Output:
// true
// true
}

View File

@@ -131,7 +131,7 @@ func (p *sshParser) parseKV() sshParserStateFn {
} }
lastHost := p.config.Hosts[len(p.config.Hosts)-1] lastHost := p.config.Hosts[len(p.config.Hosts)-1]
if strings.ToLower(key.val) == "include" { if strings.ToLower(key.val) == "include" {
inc, err := NewInclude(strings.Split(val.val, " "), comment, p.system, p.depth+1) inc, err := NewInclude(strings.Split(val.val, " "), hasEquals, key.Position, comment, p.system, p.depth+1)
if err == ErrDepthExceeded { if err == ErrDepthExceeded {
p.raiseError(val, err) p.raiseError(val, err)
return nil return nil