mirror of https://github.com/golang/go.git
net: rewrite nsswitch.conf parsing to work like other parsers
Seems simpler than having two different parsing mechanisms. Change-Id: I4f8468bc025f8e03f59ec9c79b17721581b64eed Reviewed-on: https://go-review.googlesource.com/c/go/+/448855 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Joedian Reid <joedian@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Ian Lance Taylor <iant@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
678cd71d11
commit
37ca171ce7
|
|
@ -8,7 +8,7 @@ package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"strings"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -19,7 +19,19 @@ type nssHostTest struct {
|
||||||
want hostLookupOrder
|
want hostLookupOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
func nssStr(s string) *nssConf { return parseNSSConf(strings.NewReader(s)) }
|
func nssStr(t *testing.T, s string) *nssConf {
|
||||||
|
f, err := os.CreateTemp(t.TempDir(), "nss")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := f.WriteString(s); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return parseNSSConfFile(f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
// represents a dnsConfig returned by parsing a nonexistent resolv.conf
|
// represents a dnsConfig returned by parsing a nonexistent resolv.conf
|
||||||
var defaultResolvConf = &dnsConfig{
|
var defaultResolvConf = &dnsConfig{
|
||||||
|
|
@ -45,7 +57,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
forceCgoLookupHost: true,
|
forceCgoLookupHost: true,
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("foo: bar"),
|
nss: nssStr(t, "foo: bar"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"foo.local", "myhostname", hostLookupCgo},
|
{"foo.local", "myhostname", hostLookupCgo},
|
||||||
{"google.com", "myhostname", hostLookupCgo},
|
{"google.com", "myhostname", hostLookupCgo},
|
||||||
|
|
@ -57,7 +69,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
netGo: true,
|
netGo: true,
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns files"),
|
nss: nssStr(t, "hosts: dns files"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupDNSFiles},
|
{"x.com", "myhostname", hostLookupDNSFiles},
|
||||||
},
|
},
|
||||||
|
|
@ -68,7 +80,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
netGo: true,
|
netGo: true,
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns files something_custom"),
|
nss: nssStr(t, "hosts: dns files something_custom"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupFilesDNS},
|
{"x.com", "myhostname", hostLookupFilesDNS},
|
||||||
},
|
},
|
||||||
|
|
@ -77,7 +89,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "ubuntu_trusty_avahi",
|
name: "ubuntu_trusty_avahi",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4"),
|
nss: nssStr(t, "hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"foo.local", "myhostname", hostLookupCgo},
|
{"foo.local", "myhostname", hostLookupCgo},
|
||||||
{"foo.local.", "myhostname", hostLookupCgo},
|
{"foo.local.", "myhostname", hostLookupCgo},
|
||||||
|
|
@ -92,7 +104,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
goos: "freebsd",
|
goos: "freebsd",
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("foo: bar"),
|
nss: nssStr(t, "foo: bar"),
|
||||||
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
|
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
|
||||||
},
|
},
|
||||||
// On OpenBSD, no resolv.conf means no DNS.
|
// On OpenBSD, no resolv.conf means no DNS.
|
||||||
|
|
@ -187,14 +199,14 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
goos: "linux",
|
goos: "linux",
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr(""),
|
nss: nssStr(t, ""),
|
||||||
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
|
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "files_mdns_dns",
|
name: "files_mdns_dns",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files mdns dns"),
|
nss: nssStr(t, "hosts: files mdns dns"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupFilesDNS},
|
{"x.com", "myhostname", hostLookupFilesDNS},
|
||||||
{"x.local", "myhostname", hostLookupCgo},
|
{"x.local", "myhostname", hostLookupCgo},
|
||||||
|
|
@ -204,7 +216,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "dns_special_hostnames",
|
name: "dns_special_hostnames",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns"),
|
nss: nssStr(t, "hosts: dns"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupDNS},
|
{"x.com", "myhostname", hostLookupDNS},
|
||||||
{"x\\.com", "myhostname", hostLookupCgo}, // punt on weird glibc escape
|
{"x\\.com", "myhostname", hostLookupCgo}, // punt on weird glibc escape
|
||||||
|
|
@ -217,7 +229,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
hasMDNSAllow: true,
|
hasMDNSAllow: true,
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files mdns dns"),
|
nss: nssStr(t, "hosts: files mdns dns"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupCgo},
|
{"x.com", "myhostname", hostLookupCgo},
|
||||||
{"x.local", "myhostname", hostLookupCgo},
|
{"x.local", "myhostname", hostLookupCgo},
|
||||||
|
|
@ -227,7 +239,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "files_dns",
|
name: "files_dns",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files dns"),
|
nss: nssStr(t, "hosts: files dns"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupFilesDNS},
|
{"x.com", "myhostname", hostLookupFilesDNS},
|
||||||
{"x", "myhostname", hostLookupFilesDNS},
|
{"x", "myhostname", hostLookupFilesDNS},
|
||||||
|
|
@ -238,7 +250,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "dns_files",
|
name: "dns_files",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns files"),
|
nss: nssStr(t, "hosts: dns files"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupDNSFiles},
|
{"x.com", "myhostname", hostLookupDNSFiles},
|
||||||
{"x", "myhostname", hostLookupDNSFiles},
|
{"x", "myhostname", hostLookupDNSFiles},
|
||||||
|
|
@ -249,7 +261,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "something_custom",
|
name: "something_custom",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns files something_custom"),
|
nss: nssStr(t, "hosts: dns files something_custom"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupCgo},
|
{"x.com", "myhostname", hostLookupCgo},
|
||||||
},
|
},
|
||||||
|
|
@ -258,7 +270,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "myhostname",
|
name: "myhostname",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files dns myhostname"),
|
nss: nssStr(t, "hosts: files dns myhostname"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupFilesDNS},
|
{"x.com", "myhostname", hostLookupFilesDNS},
|
||||||
{"myhostname", "myhostname", hostLookupCgo},
|
{"myhostname", "myhostname", hostLookupCgo},
|
||||||
|
|
@ -285,7 +297,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "ubuntu14.04.02",
|
name: "ubuntu14.04.02",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns mdns4"),
|
nss: nssStr(t, "hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns mdns4"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupFilesDNS},
|
{"x.com", "myhostname", hostLookupFilesDNS},
|
||||||
{"somehostname", "myhostname", hostLookupFilesDNS},
|
{"somehostname", "myhostname", hostLookupFilesDNS},
|
||||||
|
|
@ -300,7 +312,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "debian_squeeze",
|
name: "debian_squeeze",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr("hosts: dns [success=return notfound=continue unavail=continue tryagain=continue] files [notfound=return]"),
|
nss: nssStr(t, "hosts: dns [success=return notfound=continue unavail=continue tryagain=continue] files [notfound=return]"),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupDNSFiles},
|
{"x.com", "myhostname", hostLookupDNSFiles},
|
||||||
{"somehostname", "myhostname", hostLookupDNSFiles},
|
{"somehostname", "myhostname", hostLookupDNSFiles},
|
||||||
|
|
@ -310,7 +322,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
name: "resolv.conf-unknown",
|
name: "resolv.conf-unknown",
|
||||||
c: &conf{},
|
c: &conf{},
|
||||||
resolv: &dnsConfig{servers: defaultNS, ndots: 1, timeout: 5, attempts: 2, unknownOpt: true},
|
resolv: &dnsConfig{servers: defaultNS, ndots: 1, timeout: 5, attempts: 2, unknownOpt: true},
|
||||||
nss: nssStr("foo: bar"),
|
nss: nssStr(t, "foo: bar"),
|
||||||
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
|
hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}},
|
||||||
},
|
},
|
||||||
// Android should always use cgo.
|
// Android should always use cgo.
|
||||||
|
|
@ -320,7 +332,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
goos: "android",
|
goos: "android",
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr(""),
|
nss: nssStr(t, ""),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"x.com", "myhostname", hostLookupCgo},
|
{"x.com", "myhostname", hostLookupCgo},
|
||||||
},
|
},
|
||||||
|
|
@ -335,7 +347,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||||
netCgo: true,
|
netCgo: true,
|
||||||
},
|
},
|
||||||
resolv: defaultResolvConf,
|
resolv: defaultResolvConf,
|
||||||
nss: nssStr(""),
|
nss: nssStr(t, ""),
|
||||||
hostTests: []nssHostTest{
|
hostTests: []nssHostTest{
|
||||||
{"localhost", "myhostname", hostLookupFilesDNS},
|
{"localhost", "myhostname", hostLookupFilesDNS},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ package net
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"internal/bytealg"
|
"internal/bytealg"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -148,63 +147,62 @@ func (c nssCriterion) standardStatusAction(last bool) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNSSConfFile(file string) *nssConf {
|
func parseNSSConfFile(file string) *nssConf {
|
||||||
f, err := os.Open(file)
|
f, err := open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &nssConf{err: err}
|
return &nssConf{err: err}
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.close()
|
||||||
stat, err := f.Stat()
|
mtime, _, err := f.stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &nssConf{err: err}
|
return &nssConf{err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := parseNSSConf(f)
|
conf := parseNSSConf(f)
|
||||||
conf.mtime = stat.ModTime()
|
conf.mtime = mtime
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNSSConf(r io.Reader) *nssConf {
|
func parseNSSConf(f *file) *nssConf {
|
||||||
slurp, err := readFull(r)
|
|
||||||
if err != nil {
|
|
||||||
return &nssConf{err: err}
|
|
||||||
}
|
|
||||||
conf := new(nssConf)
|
conf := new(nssConf)
|
||||||
conf.err = foreachLine(slurp, func(line []byte) error {
|
for line, ok := f.readLine(); ok; line, ok = f.readLine() {
|
||||||
line = trimSpace(removeComment(line))
|
line = trimSpace(removeComment(line))
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
return nil
|
continue
|
||||||
}
|
}
|
||||||
colon := bytealg.IndexByte(line, ':')
|
colon := bytealg.IndexByteString(line, ':')
|
||||||
if colon == -1 {
|
if colon == -1 {
|
||||||
return errors.New("no colon on line")
|
conf.err = errors.New("no colon on line")
|
||||||
|
return conf
|
||||||
}
|
}
|
||||||
db := string(trimSpace(line[:colon]))
|
db := trimSpace(line[:colon])
|
||||||
srcs := line[colon+1:]
|
srcs := line[colon+1:]
|
||||||
for {
|
for {
|
||||||
srcs = trimSpace(srcs)
|
srcs = trimSpace(srcs)
|
||||||
if len(srcs) == 0 {
|
if len(srcs) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sp := bytealg.IndexByte(srcs, ' ')
|
sp := bytealg.IndexByteString(srcs, ' ')
|
||||||
var src string
|
var src string
|
||||||
if sp == -1 {
|
if sp == -1 {
|
||||||
src = string(srcs)
|
src = srcs
|
||||||
srcs = nil // done
|
srcs = "" // done
|
||||||
} else {
|
} else {
|
||||||
src = string(srcs[:sp])
|
src = srcs[:sp]
|
||||||
srcs = trimSpace(srcs[sp+1:])
|
srcs = trimSpace(srcs[sp+1:])
|
||||||
}
|
}
|
||||||
var criteria []nssCriterion
|
var criteria []nssCriterion
|
||||||
// See if there's a criteria block in brackets.
|
// See if there's a criteria block in brackets.
|
||||||
if len(srcs) > 0 && srcs[0] == '[' {
|
if len(srcs) > 0 && srcs[0] == '[' {
|
||||||
bclose := bytealg.IndexByte(srcs, ']')
|
bclose := bytealg.IndexByteString(srcs, ']')
|
||||||
if bclose == -1 {
|
if bclose == -1 {
|
||||||
return errors.New("unclosed criterion bracket")
|
conf.err = errors.New("unclosed criterion bracket")
|
||||||
|
return conf
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
criteria, err = parseCriteria(srcs[1:bclose])
|
criteria, err = parseCriteria(srcs[1:bclose])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("invalid criteria: " + string(srcs[1:bclose]))
|
conf.err = errors.New("invalid criteria: " + srcs[1:bclose])
|
||||||
|
return conf
|
||||||
}
|
}
|
||||||
srcs = srcs[bclose+1:]
|
srcs = srcs[bclose+1:]
|
||||||
}
|
}
|
||||||
|
|
@ -216,14 +214,13 @@ func parseNSSConf(r io.Reader) *nssConf {
|
||||||
criteria: criteria,
|
criteria: criteria,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return nil
|
}
|
||||||
})
|
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
// parses "foo=bar !foo=bar"
|
// parses "foo=bar !foo=bar"
|
||||||
func parseCriteria(x []byte) (c []nssCriterion, err error) {
|
func parseCriteria(x string) (c []nssCriterion, err error) {
|
||||||
err = foreachField(x, func(f []byte) error {
|
err = foreachField(x, func(f string) error {
|
||||||
not := false
|
not := false
|
||||||
if len(f) > 0 && f[0] == '!' {
|
if len(f) > 0 && f[0] == '!' {
|
||||||
not = true
|
not = true
|
||||||
|
|
@ -232,15 +229,19 @@ func parseCriteria(x []byte) (c []nssCriterion, err error) {
|
||||||
if len(f) < 3 {
|
if len(f) < 3 {
|
||||||
return errors.New("criterion too short")
|
return errors.New("criterion too short")
|
||||||
}
|
}
|
||||||
eq := bytealg.IndexByte(f, '=')
|
eq := bytealg.IndexByteString(f, '=')
|
||||||
if eq == -1 {
|
if eq == -1 {
|
||||||
return errors.New("criterion lacks equal sign")
|
return errors.New("criterion lacks equal sign")
|
||||||
}
|
}
|
||||||
lowerASCIIBytes(f)
|
if hasUpperCase(f) {
|
||||||
|
lower := []byte(f)
|
||||||
|
lowerASCIIBytes(lower)
|
||||||
|
f = string(lower)
|
||||||
|
}
|
||||||
c = append(c, nssCriterion{
|
c = append(c, nssCriterion{
|
||||||
negate: not,
|
negate: not,
|
||||||
status: string(f[:eq]),
|
status: f[:eq],
|
||||||
action: string(f[eq+1:]),
|
action: f[eq+1:],
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ubuntuTrustyAvahi = `# /etc/nsswitch.conf
|
const ubuntuTrustyAvahi = `# /etc/nsswitch.conf
|
||||||
|
|
@ -34,6 +34,8 @@ netgroup: nis
|
||||||
`
|
`
|
||||||
|
|
||||||
func TestParseNSSConf(t *testing.T) {
|
func TestParseNSSConf(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
in string
|
in string
|
||||||
|
|
@ -161,7 +163,8 @@ func TestParseNSSConf(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
gotConf := parseNSSConf(strings.NewReader(tt.in))
|
gotConf := nssStr(t, tt.in)
|
||||||
|
gotConf.mtime = time.Time{} // ignore mtime in comparison
|
||||||
if !reflect.DeepEqual(gotConf, tt.want) {
|
if !reflect.DeepEqual(gotConf, tt.want) {
|
||||||
t.Errorf("%s: mismatch\n got %#v\nwant %#v", tt.name, gotConf, tt.want)
|
t.Errorf("%s: mismatch\n got %#v\nwant %#v", tt.name, gotConf, tt.want)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,14 @@ func (f *file) readLine() (s string, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *file) stat() (mtime time.Time, size int64, err error) {
|
||||||
|
st, err := f.file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, 0, err
|
||||||
|
}
|
||||||
|
return st.ModTime(), st.Size(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func open(name string) (*file, error) {
|
func open(name string) (*file, error) {
|
||||||
fd, err := os.Open(name)
|
fd, err := os.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -236,7 +244,7 @@ func lowerASCII(b byte) byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// trimSpace returns x without any leading or trailing ASCII whitespace.
|
// trimSpace returns x without any leading or trailing ASCII whitespace.
|
||||||
func trimSpace(x []byte) []byte {
|
func trimSpace(x string) string {
|
||||||
for len(x) > 0 && isSpace(x[0]) {
|
for len(x) > 0 && isSpace(x[0]) {
|
||||||
x = x[1:]
|
x = x[1:]
|
||||||
}
|
}
|
||||||
|
|
@ -253,37 +261,19 @@ func isSpace(b byte) bool {
|
||||||
|
|
||||||
// removeComment returns line, removing any '#' byte and any following
|
// removeComment returns line, removing any '#' byte and any following
|
||||||
// bytes.
|
// bytes.
|
||||||
func removeComment(line []byte) []byte {
|
func removeComment(line string) string {
|
||||||
if i := bytealg.IndexByte(line, '#'); i != -1 {
|
if i := bytealg.IndexByteString(line, '#'); i != -1 {
|
||||||
return line[:i]
|
return line[:i]
|
||||||
}
|
}
|
||||||
return line
|
return line
|
||||||
}
|
}
|
||||||
|
|
||||||
// foreachLine runs fn on each line of x.
|
|
||||||
// Each line (except for possibly the last) ends in '\n'.
|
|
||||||
// It returns the first non-nil error returned by fn.
|
|
||||||
func foreachLine(x []byte, fn func(line []byte) error) error {
|
|
||||||
for len(x) > 0 {
|
|
||||||
nl := bytealg.IndexByte(x, '\n')
|
|
||||||
if nl == -1 {
|
|
||||||
return fn(x)
|
|
||||||
}
|
|
||||||
line := x[:nl+1]
|
|
||||||
x = x[nl+1:]
|
|
||||||
if err := fn(line); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// foreachField runs fn on each non-empty run of non-space bytes in x.
|
// foreachField runs fn on each non-empty run of non-space bytes in x.
|
||||||
// It returns the first non-nil error returned by fn.
|
// It returns the first non-nil error returned by fn.
|
||||||
func foreachField(x []byte, fn func(field []byte) error) error {
|
func foreachField(x string, fn func(field string) error) error {
|
||||||
x = trimSpace(x)
|
x = trimSpace(x)
|
||||||
for len(x) > 0 {
|
for len(x) > 0 {
|
||||||
sp := bytealg.IndexByte(x, ' ')
|
sp := bytealg.IndexByteString(x, ' ')
|
||||||
if sp == -1 {
|
if sp == -1 {
|
||||||
return fn(x)
|
return fn(x)
|
||||||
}
|
}
|
||||||
|
|
@ -327,17 +317,3 @@ func stringsEqualFold(s, t string) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFull(r io.Reader) (all []byte, err error) {
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
for {
|
|
||||||
n, err := r.Read(buf)
|
|
||||||
all = append(all, buf[:n]...)
|
|
||||||
if err == io.EOF {
|
|
||||||
return all, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue