OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[bytom/vapor.git] / vendor / github.com / miekg / dns / generate.go
diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go
new file mode 100644 (file)
index 0000000..97bc39f
--- /dev/null
@@ -0,0 +1,242 @@
+package dns
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "strconv"
+       "strings"
+)
+
+// Parse the $GENERATE statement as used in BIND9 zones.
+// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
+// We are called after '$GENERATE '. After which we expect:
+// * the range (12-24/2)
+// * lhs (ownername)
+// * [[ttl][class]]
+// * type
+// * rhs (rdata)
+// But we are lazy here, only the range is parsed *all* occurrences
+// of $ after that are interpreted.
+func (zp *ZoneParser) generate(l lex) (RR, bool) {
+       token := l.token
+       step := 1
+       if i := strings.IndexByte(token, '/'); i >= 0 {
+               if i+1 == len(token) {
+                       return zp.setParseError("bad step in $GENERATE range", l)
+               }
+
+               s, err := strconv.Atoi(token[i+1:])
+               if err != nil || s <= 0 {
+                       return zp.setParseError("bad step in $GENERATE range", l)
+               }
+
+               step = s
+               token = token[:i]
+       }
+
+       sx := strings.SplitN(token, "-", 2)
+       if len(sx) != 2 {
+               return zp.setParseError("bad start-stop in $GENERATE range", l)
+       }
+
+       start, err := strconv.Atoi(sx[0])
+       if err != nil {
+               return zp.setParseError("bad start in $GENERATE range", l)
+       }
+
+       end, err := strconv.Atoi(sx[1])
+       if err != nil {
+               return zp.setParseError("bad stop in $GENERATE range", l)
+       }
+       if end < 0 || start < 0 || end < start {
+               return zp.setParseError("bad range in $GENERATE range", l)
+       }
+
+       zp.c.Next() // _BLANK
+
+       // Create a complete new string, which we then parse again.
+       var s string
+       for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
+               if l.err {
+                       return zp.setParseError("bad data in $GENERATE directive", l)
+               }
+               if l.value == zNewline {
+                       break
+               }
+
+               s += l.token
+       }
+
+       r := &generateReader{
+               s: s,
+
+               cur:   start,
+               start: start,
+               end:   end,
+               step:  step,
+
+               file: zp.file,
+               lex:  &l,
+       }
+       zp.sub = NewZoneParser(r, zp.origin, zp.file)
+       zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
+       zp.sub.SetDefaultTTL(defaultTtl)
+       return zp.subNext()
+}
+
+type generateReader struct {
+       s  string
+       si int
+
+       cur   int
+       start int
+       end   int
+       step  int
+
+       mod bytes.Buffer
+
+       escape bool
+
+       eof bool
+
+       file string
+       lex  *lex
+}
+
+func (r *generateReader) parseError(msg string, end int) *ParseError {
+       r.eof = true // Make errors sticky.
+
+       l := *r.lex
+       l.token = r.s[r.si-1 : end]
+       l.column += r.si // l.column starts one zBLANK before r.s
+
+       return &ParseError{r.file, msg, l}
+}
+
+func (r *generateReader) Read(p []byte) (int, error) {
+       // NewZLexer, through NewZoneParser, should use ReadByte and
+       // not end up here.
+
+       panic("not implemented")
+}
+
+func (r *generateReader) ReadByte() (byte, error) {
+       if r.eof {
+               return 0, io.EOF
+       }
+       if r.mod.Len() > 0 {
+               return r.mod.ReadByte()
+       }
+
+       if r.si >= len(r.s) {
+               r.si = 0
+               r.cur += r.step
+
+               r.eof = r.cur > r.end || r.cur < 0
+               return '\n', nil
+       }
+
+       si := r.si
+       r.si++
+
+       switch r.s[si] {
+       case '\\':
+               if r.escape {
+                       r.escape = false
+                       return '\\', nil
+               }
+
+               r.escape = true
+               return r.ReadByte()
+       case '$':
+               if r.escape {
+                       r.escape = false
+                       return '$', nil
+               }
+
+               mod := "%d"
+
+               if si >= len(r.s)-1 {
+                       // End of the string
+                       fmt.Fprintf(&r.mod, mod, r.cur)
+                       return r.mod.ReadByte()
+               }
+
+               if r.s[si+1] == '$' {
+                       r.si++
+                       return '$', nil
+               }
+
+               var offset int
+
+               // Search for { and }
+               if r.s[si+1] == '{' {
+                       // Modifier block
+                       sep := strings.Index(r.s[si+2:], "}")
+                       if sep < 0 {
+                               return 0, r.parseError("bad modifier in $GENERATE", len(r.s))
+                       }
+
+                       var errMsg string
+                       mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])
+                       if errMsg != "" {
+                               return 0, r.parseError(errMsg, si+3+sep)
+                       }
+                       if r.start+offset < 0 || r.end+offset > 1<<31-1 {
+                               return 0, r.parseError("bad offset in $GENERATE", si+3+sep)
+                       }
+
+                       r.si += 2 + sep // Jump to it
+               }
+
+               fmt.Fprintf(&r.mod, mod, r.cur+offset)
+               return r.mod.ReadByte()
+       default:
+               if r.escape { // Pretty useless here
+                       r.escape = false
+                       return r.ReadByte()
+               }
+
+               return r.s[si], nil
+       }
+}
+
+// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
+func modToPrintf(s string) (string, int, string) {
+       // Modifier is { offset [ ,width [ ,base ] ] } - provide default
+       // values for optional width and type, if necessary.
+       var offStr, widthStr, base string
+       switch xs := strings.Split(s, ","); len(xs) {
+       case 1:
+               offStr, widthStr, base = xs[0], "0", "d"
+       case 2:
+               offStr, widthStr, base = xs[0], xs[1], "d"
+       case 3:
+               offStr, widthStr, base = xs[0], xs[1], xs[2]
+       default:
+               return "", 0, "bad modifier in $GENERATE"
+       }
+
+       switch base {
+       case "o", "d", "x", "X":
+       default:
+               return "", 0, "bad base in $GENERATE"
+       }
+
+       offset, err := strconv.Atoi(offStr)
+       if err != nil {
+               return "", 0, "bad offset in $GENERATE"
+       }
+
+       width, err := strconv.Atoi(widthStr)
+       if err != nil || width < 0 || width > 255 {
+               return "", 0, "bad width in $GENERATE"
+       }
+
+       if width == 0 {
+               return "%" + base, offset, ""
+       }
+
+       return "%0" + widthStr + base, offset, ""
+}