// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Simple file i/o and string manipulation, to avoid // depending on strconv and bufio and strings. package adns import ( "io" "os" "time" "github.com/mjl-/adns/internal/bytealg" ) type file struct { file *os.File data []byte atEOF bool } func (f *file) close() { f.file.Close() } func (f *file) getLineFromData() (s string, ok bool) { data := f.data i := 0 for i = 0; i < len(data); i++ { if data[i] == '\n' { s = string(data[0:i]) ok = true // move data i++ n := len(data) - i copy(data[0:], data[i:]) f.data = data[0:n] return } } if f.atEOF && len(f.data) > 0 { // EOF, return all we have s = string(data) f.data = f.data[0:0] ok = true } return } func (f *file) readLine() (s string, ok bool) { if s, ok = f.getLineFromData(); ok { return } if len(f.data) < cap(f.data) { ln := len(f.data) n, err := io.ReadFull(f.file, f.data[ln:cap(f.data)]) if n >= 0 { f.data = f.data[0 : ln+n] } if err == io.EOF || err == io.ErrUnexpectedEOF { f.atEOF = true } } s, ok = f.getLineFromData() 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) { fd, err := os.Open(name) if err != nil { return nil, err } return &file{fd, make([]byte, 0, 64*1024), false}, nil } func stat(name string) (mtime time.Time, size int64, err error) { st, err := os.Stat(name) if err != nil { return time.Time{}, 0, err } return st.ModTime(), st.Size(), nil } // Count occurrences in s of any bytes in t. func countAnyByte(s string, t string) int { n := 0 for i := 0; i < len(s); i++ { if bytealg.IndexByteString(t, s[i]) >= 0 { n++ } } return n } // Split s at any bytes in t. func splitAtBytes(s string, t string) []string { a := make([]string, 1+countAnyByte(s, t)) n := 0 last := 0 for i := 0; i < len(s); i++ { if bytealg.IndexByteString(t, s[i]) >= 0 { if last < i { a[n] = s[last:i] n++ } last = i + 1 } } if last < len(s) { a[n] = s[last:] n++ } return a[0:n] } func getFields(s string) []string { return splitAtBytes(s, " \r\t\n") } // Bigger than we need, not too big to worry about overflow const big = 0xFFFFFF // Decimal to integer. // Returns number, characters consumed, success. func dtoi(s string) (n int, i int, ok bool) { n = 0 for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { n = n*10 + int(s[i]-'0') if n >= big { return big, i, false } } if i == 0 { return 0, 0, false } return n, i, true } // hasUpperCase tells whether the given string contains at least one upper-case. func hasUpperCase(s string) bool { for i := range s { if 'A' <= s[i] && s[i] <= 'Z' { return true } } return false } // lowerASCIIBytes makes x ASCII lowercase in-place. func lowerASCIIBytes(x []byte) { for i, b := range x { if 'A' <= b && b <= 'Z' { x[i] += 'a' - 'A' } } } // lowerASCII returns the ASCII lowercase version of b. func lowerASCII(b byte) byte { if 'A' <= b && b <= 'Z' { return b + ('a' - 'A') } return b } // trimSpace returns x without any leading or trailing ASCII whitespace. func trimSpace(x string) string { for len(x) > 0 && isSpace(x[0]) { x = x[1:] } for len(x) > 0 && isSpace(x[len(x)-1]) { x = x[:len(x)-1] } return x } // isSpace reports whether b is an ASCII space character. func isSpace(b byte) bool { return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // removeComment returns line, removing any '#' byte and any following // bytes. func removeComment(line string) string { if i := bytealg.IndexByteString(line, '#'); i != -1 { return line[:i] } return line } // foreachField runs fn on each non-empty run of non-space bytes in x. // It returns the first non-nil error returned by fn. func foreachField(x string, fn func(field string) error) error { x = trimSpace(x) for len(x) > 0 { sp := bytealg.IndexByteString(x, ' ') if sp == -1 { return fn(x) } if field := trimSpace(x[:sp]); len(field) > 0 { if err := fn(field); err != nil { return err } } x = trimSpace(x[sp+1:]) } return nil } // stringsHasSuffix is strings.HasSuffix. It reports whether s ends in // suffix. func stringsHasSuffix(s, suffix string) bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } // stringsHasSuffixFold reports whether s ends in suffix, // ASCII-case-insensitively. func stringsHasSuffixFold(s, suffix string) bool { return len(s) >= len(suffix) && stringsEqualFold(s[len(s)-len(suffix):], suffix) } // stringsHasPrefix is strings.HasPrefix. It reports whether s begins with prefix. func stringsHasPrefix(s, prefix string) bool { return len(s) >= len(prefix) && s[:len(prefix)] == prefix } // stringsEqualFold is strings.EqualFold, ASCII only. It reports whether s and t // are equal, ASCII-case-insensitively. func stringsEqualFold(s, t string) bool { if len(s) != len(t) { return false } for i := 0; i < len(s); i++ { if lowerASCII(s[i]) != lowerASCII(t[i]) { return false } } return true }